1. Introduction
In today’s competitive market, understanding customer behavior is
crucial for driving profitability and long-term success. By analyzing
transaction data, businesses can gain insights into key factors such as
customer demographics, marketing channel
performance, and profitability trends. These
insights enable businesses to make data-driven decisions to enhance
customer retention, optimize marketing strategies, and increase overall
revenue.
This project leverages a comprehensive dataset to explore these
dynamics, focusing on actionable outcomes and recommendations.
Goal
The primary goal of this project is to analyze customer
behavior and profitability in relation to
various marketing channels. By leveraging the dataset, which includes
essential customer and transaction details, we aim to uncover factors
that drive Customer Lifetime Value (CLV) and measure
the effectiveness of different marketing strategies.
Through this analysis, the business will gain actionable insights to:
- Optimize marketing efforts. - Improve customer retention. - Increase
overall profits.
Deliverables
2. Exploratory Data Analysis (EDA)
- Perform statistical analysis and visualizations to:
- Identify trends, relationships, and patterns.
- Highlight anomalies or unusual behaviors in the data.
3. Outlier Detection
- Purpose: Detect unusual patterns in customer
transactions, such as:
- Exceptionally high profits
- Promotions or discounts
- Unusual order behavior
- Goal: Understand exceptional cases, refine
marketing strategies, and manage both high-value and low-value customers
effectively.
Summary
This analysis will empower the business to: - Understand its customer
base more deeply. - Tailor marketing strategies to maximize engagement
and profitability. - Refine campaigns to improve customer
satisfaction and long-term growth.
By focusing on these objectives, the business can turn data-driven
insights into impactful actions.
3. Explatory Data Analysis
Histogram Analysis
This section presents the histograms for the following six key
variables from the dataset:
- First Order Profit
- Subsequent Order Profit
- Subsequent Orders Count
- Total Value of All Promotions
- Age
- Total Profit
These histograms provide insights into the distributions of the data,
helping us identify skewness, the presence of outliers, and potential
data transformations.
# Select specific columns
selected_cols <- c(
"First_Order_Profit", "Subsequent_Order_Profit", "Subsequent_Orders_Count",
"Total_value_of_all_promotions", "Age", "Total_Profit"
)
# Automatically extract the required number of colors
colors <- get_rose_pine_colors(length(selected_cols))
# Prepare the data list
data_list <- lapply(selected_cols, function(col) df[[col]])
names(data_list) <- selected_cols
# Generate Histograms Dynamically
histograms <- lapply(seq_along(data_list), function(i) {
formatted_title <- tools::toTitleCase(gsub("_", " ", names(data_list)[i])) # Format column name
ggplot(data.frame(value = data_list[[i]]), aes(x = value)) +
geom_histogram_default(data_length = length(data_list[[i]]), fill_color = colors[i]) + # Dynamic bins & color
labs(
title = paste("", formatted_title), # Use formatted title
x = "",
y = "Frequency"
) +
theme_rose_pine()
})
# Combine All Histograms into a Grid Layout
do.call(grid.arrange, c(histograms, ncol = 3))

Observations and Recommendations
1. First Order Profit
- Observation:
- Highly right-skewed distribution with most values near zero.
- A long tail suggests high-profit outliers.
- Recommendation:
- Apply a logarithmic transformation to reduce skewness for
analysis.
- Investigate outliers to determine their validity or consider capping
them.
2. Subsequent Order Profit
- Observation:
- Right-skewed with most values near zero and a few very high
profits.
- The distribution is similar to the First Order Profit.
- Recommendation:
- Use similar treatment as First Order Profit (e.g., logarithmic
transformation or outlier capping).
- Explore customer segments that generate high subsequent
profits.
3. Subsequent Orders Count
- Observation:
- Majority of customers placed very few subsequent orders (≤
5).
- A sharp decline in customer count as the number of orders
increases.
- Recommendation:
- Group higher order counts into bins for better visual
interpretability.
- Investigate factors influencing customers with higher order counts
(e.g., age, promotions).
5. Age
- Observation:
- Approximately bell-shaped, with most customers falling between 20–50
years of age.
- Fewer customers are younger than 20 or older than 50.
- Recommendation:
- No immediate action required; the distribution is balanced and can
be used as-is for modeling.
6. Total Profit
- Observation:
- Similar to profit variables, the distribution is right-skewed with
most values concentrated near zero.
- High-profit outliers are present.
- Recommendation:
- Investigate high-profit customers and their characteristics.
- Consider transformations or outlier handling for modeling
purposes.
This analysis helps identify potential preprocessing steps and guides
exploratory analysis to better understand customer behavior.
Density Plot Analysis
This section provides observations and recommendations based on the
density plots of the following variables:
- First Order Profit
- Subsequent Order Profit
- Subsequent Orders Count
- Total Value of All Promotions
- Age
- Total Profit
# Generate KDE plots dynamically
kde <- lapply(seq_along(data_list), function(i) {
formatted_title <- tools::toTitleCase(gsub("_", " ", names(data_list)[i]))
ggplot(data.frame(value = data_list[[i]]), aes(x = value)) +
geom_density_default(fill_color = colors[i]) + # Dynamic color assignment
labs(
title = paste("", formatted_title),
x = "",
y = "Density"
) +
theme_rose_pine()
})
# Combine all plots into a grid layout
do.call(grid.arrange, c(kde, ncol = 3))

Observations and Recommendations
1. First Order Profit
- Observation:
- Right-skewed distribution with a sharp peak at low profit
values.
- Long tail due to high-profit customers.
- Recommendation:
- Apply a logarithmic transformation for skewness correction.
- Examine outliers for validity or apply capping to reduce
influence.
2. Subsequent Order Profit
- Observation:
- Sharp peak at low values, with a similar right-skewed trend as First
Order Profit.
- Long tail extending towards very high values.
- Recommendation:
- Consider handling outliers through capping or Winsorization.
- Investigate customer segments contributing to high subsequent
profits.
3. Subsequent Orders Count
- Observation:
- Clear peak at 1–2 orders, indicating most customers made few repeat
purchases.
- Distribution flattens significantly for higher order counts.
- Recommendation:
- Group higher counts into bins for clearer insights.
- Explore factors (e.g., demographics, promotions) influencing
customers with many orders.
5. Age
- Observation:
- Distribution is approximately bell-shaped, centered around 30–40
years.
- Skewness is minimal, with balanced data for most age ranges.
- Recommendation:
- No transformation needed. The variable can be used as-is in
analysis.
6. Total Profit
- Observation:
- Similar right-skewed trend as other profit variables, with a peak at
low values.
- Long tail shows the presence of a few high-profit customers.
- Recommendation:
- Consider transformations to address skewness and reduce outlier
effects.
- Investigate high-profit customers to understand their
characteristics.
This density plot analysis highlights key trends and provides
actionable recommendations for preprocessing and further
exploration.
Gender Distribution Analysis
This section visualizes the distribution of gender across the dataset
using a bar plot and a pie chart.
gender_counts <- as.data.frame(table(df$Gender))
names(gender_counts) <- c("Gender", "Count")
# Create Count Plot using geom_count_default
count_plot <- ggplot(gender_counts, aes(x = Gender, y = Count, fill = Gender)) +
geom_bar(stat = "identity") +
theme_rose_pine() + # Apply the custom theme
scale_fill_rose_pine() + # Apply the fill color scale
labs(title = "Gender Distribution Plot", x = "Gender", y = "Count") + # Title and axis labels
theme(
plot.title = element_text(size = 18, face = "bold", color = "white"), # Title font size and color
axis.title.x = element_text(size = 14, color = "white"), # X-axis label font size and color
axis.title.y = element_text(size = 14, color = "white"), # Y-axis label font size and color
axis.text = element_text(size = 12, color = "white"), # Axis tick labels font size and color
legend.position = "right",
legend.title = element_text(size = 13, color = "white"), # Legend title font size and color
legend.text = element_text(size = 11, color = "white"), # Legend text font size and color
legend.key.size = unit(1, "cm") # Adjust the size of legend keys (the color boxes)
)
# Generate the Pie Chart
pie_chart <- geom_pie_default(
data = gender_counts,
category_col = "Gender",
value_col = "Count"
) +
theme(
legend.position = "none",
)
# Combine the plots using grid.arrange
grid.arrange(count_plot, pie_chart, ncol = 2)

Observations
1. Bar Plot
- Observation:
- The majority of customers are Male, making up the
largest group.
- A smaller proportion of customers are Female.
- A notable percentage of customers fall into the Not
Defined category, which might indicate missing or unspecified
data.
2. Pie Chart
- Observation:
- Male customers constitute approximately
63.5% of the dataset.
- Female customers account for
23.5%.
- The Not Defined category represents
13%.
Recommendations
- Investigate the Not Defined category to determine
whether this data can be clarified or excluded.
- Use this distribution to segment analysis or marketing strategies
based on gender.
Marketing Channel Type Distribution
This section visualizes the distribution of marketing channels,
providing insights into which channels are most effective in acquiring
customers.
marketing_channel_type_count <- as.data.frame(table(df$Marketing_Channel_Type))
names(marketing_channel_type_count) <- c("Marketing_Channel_Type", "Count")
# Create Count Plot using geom_count_default
count_plot <- ggplot(marketing_channel_type_count, aes(x = Marketing_Channel_Type, y = Count, fill = Marketing_Channel_Type)) +
geom_bar(stat = "identity") +
theme_rose_pine() + # Apply the custom theme
scale_fill_rose_pine() + # Apply the fill color scale
labs(title = "Marketing Channel Type Distribution", x = "", y = "Count") + # Title and axis labels
theme(
plot.title = element_text(size = 18, face = "bold", color = "white"), # Title font size and color
#axis.title.x = element_text(size = 14, color = "white"), # X-axis label font size and color
axis.text.x = element_blank(), # Hide x-axis text
axis.title.y = element_text(size = 14, color = "white"), # Y-axis label font size and color
axis.text = element_text(size = 12, color = "white", angle = 90), # Axis tick labels font size and color
legend.position = "right",
legend.title = element_text(size = 13, color = "white"), # Legend title font size and color
legend.text = element_text(size = 11, color = "white"), # Legend text font size and color
legend.key.size = unit(1, "cm") # Adjust the size of legend keys (the color boxes)
)
# Generate the Pie Chart
pie_chart <- geom_pie_default(
data = marketing_channel_type_count,
category_col = "Marketing_Channel_Type",
value_col = "Count"
) +
theme(
legend.position = "none",
)
# Combine the plots using grid.arrange
grid.arrange(count_plot, pie_chart, ncol = 2)

Observations
1. Bar Plot
- Observation:
- The Direct channel contributes the largest share,
with approximately 35.8% of customers.
- Organic Search and Paid Search are
the second and third most common channels, at 26.7% and
20.8%, respectively.
- Affiliates represent 12.3%, and
Paid Social contributes the smallest share at
4.5%.
2. Pie Chart
- Observation:
- The proportional view confirms that Direct,
Organic Search, and Paid Search are
the dominant channels.
- Paid Social, despite being a minor channel, might
target niche customer segments.
Recommendations
- Focus marketing efforts on Direct and
Organic Search channels, as they have a significant
impact.
- Evaluate the performance of Paid Social to
determine whether it is cost-effective or requires adjustments.
- Explore opportunities to improve customer acquisition through
Affiliates by refining the strategy.
Top 20 Location Count Analysis
This section explores the distribution of customer counts across the
top 20 locations in the dataset.
# Calculate Gender Counts (Location Counts)
location_counts <- data.frame(table(df$Location))
names(location_counts) <- c("Location", "Count")
# Filter for the top 20 locations by count
top_20_locations <- location_counts %>%
arrange(desc(Count)) %>%
head(20)
# Count Plot for Top 20 Locations
count_plot <- ggplot(top_20_locations, aes(x = Location, y = Count, fill = Location)) +
geom_bar(stat = "identity") +
theme_rose_pine() + # Apply the custom theme
scale_fill_rose_pine() + # Apply the fill color scale
labs(title = "Top 20 Location Count Plot", x = "Location", y = "Count") + # Title and axis labels
theme(legend.position = "none",
axis.text.x = element_text(angle = 90, hjust = 1)) # Rotate x-axis labels for better readability
# Show the count plot
count_plot

Observations
Bar Plot
- Observation:
- Dublin dominates the dataset, contributing the
highest count of customers, significantly surpassing all other
locations.
- Other locations such as Cork,
Galway, and Limerick contribute a
smaller but noticeable count.
- A long tail exists for other locations, indicating a relatively
small representation from these areas.
Recommendations
- Given Dublin’s significant contribution, marketing and business
strategies should focus heavily on this region.
- For smaller locations, consider regional campaigns or promotions to
increase customer engagement.
- Evaluate if the concentration in Dublin skews the dataset or limits
insights into other regions.
Box Plot Analysis
This section examines the distribution and presence of outliers
across six key variables using box plots. The visualizations provide a
clear understanding of data variability and potential anomalies.
# Generate Boxplots Dynamically
box_plot_list <- lapply(seq_along(data_list), function(i) {
formatted_title <- tools::toTitleCase(gsub("_", " ", names(data_list)[i]))
ggplot(df, aes(x = factor(1), y = data_list[[i]])) +
geom_boxplot_default(data = df, x_col = "factor(1)", y_col = names(data_list)[i], fill_color = colors[i]) + # Dynamic colors
labs(
title = paste("", formatted_title),
x = "Category",
y = "Value"
) +
theme_rose_pine() + # Apply Rose Pine theme
theme(
axis.text.x = element_blank(), # Remove x-axis text
axis.ticks.x = element_blank(), # Remove x-axis ticks
plot.title = element_text(size = 10, face = "bold") # Title formatting
)
})
# Combine All Boxplots into a Grid Layout using grid.arrange
grid.arrange(grobs = box_plot_list, ncol = length(selected_cols)) # Arrange plots horizontally

Observations
1. First Order Profit
- Observation:
- The majority of data points are clustered below 20
units.
- A few extreme outliers extend up to 60 units,
suggesting a skewed distribution.
2. Subsequent Order Profit
- Observation:
- Most values are below 100 units, with significant
outliers reaching beyond 400 units.
- Highlights variability in profit generation from subsequent
orders.
3. Subsequent Orders Count
- Observation:
- Data is tightly clustered below 10 orders, with a
few outliers exceeding 30 orders.
5. Age
- Observation:
- Most customers are between 20 and 50 years
old.
- Few outliers extend beyond 80 years, which could
indicate data inconsistencies or older customers.
6. Total Profit
- Observation:
- The bulk of data is below 100 units.
- Outliers extend beyond 400 units, pointing to
high-value customers or data irregularities.
Recommendations
- Outlier Treatment: Consider winsorizing or
transforming data to minimize the influence of extreme outliers.
- Data Quality Check: Verify the accuracy of outlier
data points, particularly for variables like Age and
Total Value of All Promotions.
- Further Analysis: Investigate the relationship
between high outliers in Total Profit and promotional
activity.
Age Distribution by Gender
This section explores the distribution of customers’ ages, segmented
by gender. The visualization provides insights into age demographics and
highlights gender-based trends.
ggplot(df, aes(x = Age, fill = Gender)) + # Group by Gender
geom_histogram(binwidth = 0.5, position = "dodge") +
labs(
title = "Age Distribution by Gender",
x = "Age",
y = "Frequency"
) +
theme_rose_pine() +
scale_fill_rose_pine()

Observations
- Overall Age Distribution:
- The majority of customers fall between 20 and 50
years.
- A sharp decline in frequency is observed beyond 50
years, with sparse data for customers above 80
years.
- Gender Segmentation:
- Males form the largest segment across all age
groups.
- Females represent a smaller but consistent portion
across most age groups.
- Not Defined gender has minimal representation,
primarily in the younger age brackets.
- Peaks in Age:
- Noticeable peaks around 25–30 years, indicating a
concentrated customer base in this age range.
Recommendations
- Target Marketing:
- Focus marketing efforts on the 20-50 age group, as
it constitutes the majority of the customer base.
- Data Validation:
- Ensure accuracy for customers with undefined gender or those above
80 years, as these may indicate data entry errors.
Total Profit Analysis
This section provides insights into total profit distribution across
different genders and marketing channel types. The bar plots illustrate
the total profit contribution for each category.
# Plot 1: Total Profit by Gender
plot_gender <- ggplot(df, aes(x = factor(Gender), y = Total_Profit, fill = Gender)) +
geom_bar(stat = "summary", fun = "sum", position = "dodge") + # Summing the total profit by gender
labs(
title = "Total Profit by Gender",
x = "Gender",
y = "Total Profit"
) +
theme_rose_pine() +
scale_fill_rose_pine() +
theme(legend.position = "top") # Move legend to the top
# Plot 2: Total Profit by Marketing Channel Type
plot_marketing_channel <- ggplot(df, aes(x = factor(Marketing_Channel_Type), y = Total_Profit, fill = Marketing_Channel_Type)) +
geom_bar(stat = "summary", fun = "sum", position = "dodge") + # Summing total profit by marketing channel
labs(
title = "Total Profit by Marketing Channel Type",
x = "Marketing Channel Type",
y = "Total Profit"
) +
theme_rose_pine() +
scale_fill_rose_pine() +
theme(legend.position = "top") # Move legend to the top
# Merge the three plots into a grid layout
grid.arrange(plot_gender, plot_marketing_channel, ncol = 2)

Observations
Total Profit by Gender
- Male Dominance:
- Male customers contribute the highest total profit compared to other
genders.
- Female customers show a significant contribution, but it is still
much lower than male customers.
- The “Not Defined” gender category contributes the least to total
profit.
- Implications:
- Marketing strategies targeted toward male customers appear to be
more effective in driving profits.
- Potential opportunity exists to improve engagement and profit
generation from female customers.
Total Profit by Marketing Channel Type
- Top Performing Channels:
- Direct marketing channels contribute the highest
total profit, showcasing the effectiveness of direct customer
engagement.
- Organic search also performs well, indicating the
importance of SEO and organic traffic in driving profitability.
- Underperforming Channels:
- Paid social and affiliates
channels contribute the least to total profit, suggesting the need to
evaluate and optimize strategies for these channels.
- Implications:
- Businesses should prioritize resources on direct and organic
channels for maximum profitability.
- Evaluate the ROI for underperforming channels and refine strategies
for better returns.
# Summarize percentage distribution of Gender by Marketing_Channel_Type
gender_channel_distribution <- df %>%
group_by(Gender, Marketing_Channel_Type) %>%
summarise(Count = n()) %>%
group_by(Marketing_Channel_Type) %>%
mutate(Percentage = (Count / sum(Count)) * 100)
# Pivot data for heatmap
heatmap_data <- gender_channel_distribution %>%
select(Gender, Marketing_Channel_Type, Percentage) %>%
pivot_wider(names_from = Marketing_Channel_Type, values_from = Percentage)
# Melt the data for ggplot
melted_data <- melt(heatmap_data, id.vars = "Gender", variable.name = "Marketing_Channel_Type", value.name = "Percentage")
# Plot the heatmap with `scale_fill_gradient2` and Rosé Pine colors
ggplot(melted_data, aes(x = Marketing_Channel_Type, y = Gender, fill = Percentage)) +
geom_tile() +
geom_text(aes(label = sprintf("%.2f", Percentage)), size = 6) + # Show percentage with 2 decimals
scale_fill_gradient2(
low = colors[2], # Low color
mid = colors[1], # Mid color
high = colors[3], # High color
midpoint = 0.5, # Assume midpoint at 50% for percentages
limits = c(0, 100), # Set limits for percentage
name = "Percentage"
) +
theme_rose_pine(base_size = 14) +
labs(
title = "Gender Distribution by Marketing Channel Type (%)",
x = "Marketing Channel Type",
y = "Gender"
) +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 19), # Center and bold title
axis.text.x = element_text(angle = 45, hjust = 1, size = 14), # Rotate x-axis labels
axis.text.y = element_text(size = 14), # Adjust y-axis text size
legend.position = "right", # Place the legend on the right
legend.text = element_text(size = 12), # Adjust legend text size
legend.title = element_text(size = 13, face = "bold") # Adjust legend title size
)

Locations by Total Profit and Gender
This visualization presents the distribution of total profit across
different locations, segmented by gender (Female, Male, and Not
Defined). The analysis focuses on identifying geographical profitability
trends.
# Summarize the total profit by Location
location_rev <- df %>%
group_by(Location) %>%
summarise(Total_Profit = sum(Total_Profit, na.rm = TRUE)) %>%
arrange(Total_Profit) %>%
mutate(Location = factor(Location, levels = .$Location))
# Summarize total profit by Location and Gender
location_gender_rev <- df %>%
group_by(Location, Gender) %>%
summarise(Total_Profit = sum(Total_Profit, na.rm = TRUE)) %>%
ungroup() %>%
mutate(Location = factor(Location, levels = location_rev$Location))
# Plot the data for the top 20 locations by Total_Profit and Gender
ggplot(location_gender_rev, aes(Location, Total_Profit, fill = Gender)) +
geom_bar(stat = "identity", position = "dodge") +
coord_flip() +
facet_wrap(~ Gender) +
theme_rose_pine() + # Apply the custom theme
scale_fill_rose_pine() + # Apply the fill color scale
labs(title = "Locations by Total Profit and Gender", x = "Location", y = "Total Profit") +
theme(legend.position = "top") # Move the legend to the top

Observations
- Overall Insights:
- Dublin significantly outperforms all other
locations in total profit contributions, irrespective of gender.
- Locations such as Cork, Galway,
and Limerick follow Dublin, but their profit
contributions are considerably lower.
- Gender-Based Distribution:
- Male customers dominate the total profit
contributions across almost all locations, highlighting their
significant role in revenue generation.
- Female customers show notable contributions in
Dublin and a few other top cities but lag behind male customers
overall.
- The Not Defined gender group has minimal profit
contributions and is mostly concentrated in a few locations.
- Geographical Distribution:
- A long tail of smaller towns contributes marginally to total profit.
These locations could indicate untapped potential or low market
penetration.
- High concentration of profit in major cities like Dublin suggests a
strong urban customer base.
Recommendations
- Focus marketing and customer engagement efforts on high-profit
cities such as Dublin, Cork, and
Galway.
- Develop strategies to increase female customer engagement in smaller
towns and cities.
- Investigate the “Not Defined” gender group to understand its
characteristics and potential for growth.
- Explore opportunities in smaller towns with low profit contributions
to expand market reach.
5. Handling Outliers
When dealing with outlier removal, two popular IQR-based methods
are:
- Column-Wise Filtering: Removes outliers for each
column individually.
- Row-Wise Filtering: Removes entire rows if any
column contains an outlier.
This document explores their characteristics, advantages,
disadvantages, and use cases.
Column-Wise Filtering
Column-Wise Filtering is a less aggressive approach to outlier
removal. It detects and removes outliers from each column
individually while retaining rows that are valid for
other columns. This method is especially useful for exploratory data
analysis (EDA), where retaining as much data as possible is
essential.
remove_outliers_columnwise <- function(data, cols) {
for (col in cols) {
Q1 <- quantile(data[[col]], 0.25, na.rm = TRUE)
Q3 <- quantile(data[[col]], 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
data <- data %>% filter(data[[col]] >= lower_bound & data[[col]] <= upper_bound)
}
return(data)
}
Example Execution
cleaned_df_columnwise <- remove_outliers_columnwise(df, selected_cols)
cat("Original Rows:", nrow(df))
Original Rows: 30591
cat("Cleaned Rows:", nrow(cleaned_df_columnwise))
Cleaned Rows: 24180
Characteristics of Column-Wise Filtering
How It Works
- Outliers are detected and removed for each column
independently.
- Rows are retained unless flagged as an outlier in the
current column being processed.
Advantages
- Retains more data overall, as rows valid in other columns are
not removed.
- Less aggressive, making it suitable for exploratory data
analysis (EDA) or initial cleaning steps.
Disadvantages
- Inconsistent cleaning: A row flagged as an outlier in one column but
valid in another remains, potentially leading to inconsistent
results.
Row-Wise Filtering
remove_outliers_iqr <- function(data, cols) {
data %>%
filter(across(all_of(cols), ~ {
Q1 <- quantile(., 0.25, na.rm = TRUE)
Q3 <- quantile(., 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
. >= lower_bound & . <= upper_bound
}))
}
Example Execution
# Example: Applying Row-Wise Filtering
cleaned_df_rowwise <- remove_outliers_iqr(df, selected_cols)
# Output: Before and After Row Counts
cat("Original Rows:", nrow(df))
Original Rows: 30591
cat("Cleaned Rows:", nrow(cleaned_df_rowwise))
Cleaned Rows: 25869
Characteristics of Row-Wise Filtering
How It Works
- Outliers are detected across all selected columns at the
same time.
- If any column in a row contains an outlier, the
entire row is removed.
Advantages
- Ensures consistent cleaning across all selected
columns.
- Suitable for machine learning pipelines or
strict statistical analysis, where clean and complete
data is crucial.
Disadvantages
- More aggressive: This approach often leads to significant
data loss when variability exists across multiple columns.
Which One Should You Use?
Selecting the appropriate outlier removal method depends on your
goal. Here’s a straightforward guide to help you decide.
If Your Goal is Exploratory Data Analysis (EDA) or You Want
to Retain as Much Data as Possible:
- Use:
remove_outliers_columnwise
- Why Choose This Method?
- This approach is less aggressive, removing outliers
column by column.
- Rows that are valid in other columns remain intact, allowing you to
retain more of your dataset.
- Perfect for initial data cleaning or
early-stage exploratory data analysis (EDA), where
preserving data for a broader overview is essential.
If Your Goal is Machine Learning or Statistical Modeling,
Where Consistency Across Rows is Critical:
- Use:
remove_outliers_iqr
- Why Choose This Method?
- This method is more stringent, removing entire rows
if any column contains an outlier.
- Ensures that your dataset is consistent and free
from outliers in the specified columns.
- Ideal for machine learning pipelines or
statistical modeling, where clean and consistent data
is crucial for accurate results.
Quick Comparison
| Exploratory Data Analysis (EDA) |
remove_outliers_columnwise |
Retains more data for exploration. |
| Machine Learning/Modeling |
remove_outliers_iqr |
Ensures strict consistency across rows. |
Final Thoughts
- Use Column-Wise Filtering
(
remove_outliers_columnwise) when exploring data and aiming
to preserve as much information as possible.
- Use Row-Wise Filtering
(
remove_outliers_iqr) when consistency across rows is
critical for downstream tasks like modeling or analysis.
Note: Always evaluate the impact of your chosen method on the dataset
to ensure it aligns with your goals.
Violin Plots Column-Wise Filtering
Violin plots provide a detailed representation of the distribution of
data, combining the information of a box plot and a density plot. This
visualization is particularly useful for identifying patterns, spread,
and potential outliers within a dataset.
The following violin plots display the distributions of key features
in the dataset after applying column-wise outlier removal using
the IQR method. The selected features include:
- First Order Profit
- Subsequent Order Profit
- Subsequent Orders Count
- Total Value of All Promotions
- Age
- Total Profit
# Step 3: Generate Violin Plots Dynamically
violin_plot_list <- lapply(seq_along(selected_cols), function(i) {
col_name <- selected_cols[i]
formatted_title <- tools::toTitleCase(gsub("_", " ", col_name))
ggplot(cleaned_df_columnwise, aes(x = factor(1), y = .data[[col_name]])) +
geom_violin(fill = colors[i], color = colors[i], amount = 0.2) + # Dynamic colors
labs(
title = paste("", formatted_title),
x = "",
y = "Value"
) +
theme_rose_pine() + # Apply Rose Pine theme
theme(
axis.text.x = element_blank(), # Remove x-axis text
axis.ticks.x = element_blank(), # Remove x-axis ticks
plot.title = element_text(size = 11, face = "bold") # Title formatting
)
})
# Step 4: Combine All Violin Plots into a Grid Layout
grid.arrange(grobs = violin_plot_list, ncol = length(selected_cols))

Observations
First Order Profit
- The majority of the values are concentrated within the lower range,
indicating most customers’ first orders yield relatively low
profit.
- A small tail suggests a few customers have significantly higher
profits.
Subsequent Order Profit
- Similar to the first order, the density is concentrated at the lower
end with fewer customers contributing to higher profits.
Subsequent Orders Count
- Most customers have placed a limited number of subsequent
orders.
- The distribution reveals a small number of customers with many
subsequent orders.
Age
- Age distribution is fairly uniform in the middle range (20–40
years), tapering off for younger and older customers.
Total Profit
- The overall profit is primarily low, with a few customers
contributing to high profits.
Advantages of Violin Plots
- Combines the features of a box plot and density plot, making it
easier to identify the spread and density of data.
- Highlights potential outliers visually while retaining the overall
shape of the distribution.
Limitations
- Does not provide a direct count of outliers.
- Interpretation can be challenging for features with highly skewed
distributions.
Conclusion
The violin plots offer a comprehensive overview of the cleaned data
distributions, highlighting the concentration of values, the spread, and
the presence of potential outliers. These visualizations serve as a
valuable tool for exploratory data analysis (EDA) and preparing data for
further modeling.
# Subset the cleaned dataset to the selected columns
correlation_data <- cleaned_df_columnwise[, selected_cols]
# Compute the correlation matrix
correlation_matrix <- cor(correlation_data, use = "pairwise.complete.obs")
# Melt the correlation matrix for ggplot
correlation_melted <- melt(correlation_matrix)
# Generate the correlation heatmap
ggplot(data = correlation_melted, aes(x = Var1, y = Var2, fill = value)) +
geom_tile() + # Add white border for tiles
geom_text(aes(label = round(value, 2)), size = 6) + # Add correlation coefficients
scale_fill_gradient2(
low = colors[3],
high = colors[1],
mid = colors[2],
midpoint = 0,
limits = c(-1, 1),
guide = guide_colorbar(
title = "",
barwidth = 40, # Make the color bar wider
barheight = 1 # Adjust the height of the bar
)
) +
theme_rose_pine() + # Apply the Rosé Pine theme
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 16),
axis.text.y = element_text(size = 16),
plot.title = element_text(size = 20, face = "bold", hjust = 0.28), # Center and adjust title size
axis.title = element_blank(), # Remove axis titles
panel.grid = element_blank(), # Remove grid lines
legend.position = "top",
legend.text = element_text(size = 11), # Adjust legend text size
)+
ggtitle("Correlation Matrix") # Add plot title

# Select categorical columns
categorical_cols <- c("Gender", "Location", "Contact_Allowed", "Marketing_Channel_Type")
# Subset the dataset
categorical_data <- cleaned_df_columnwise[, categorical_cols]
# Define a function to compute Cramér's V for a pair of categorical features
cramers_v_matrix <- function(data) {
n <- ncol(data)
matrix <- matrix(NA, n, n, dimnames = list(names(data), names(data)))
for (i in seq_len(n)) {
for (j in seq_len(n)) {
if (i <= j) {
matrix[i, j] <- cramersV(data[[i]], data[[j]], simulate.p.value = TRUE)
} else {
matrix[i, j] <- matrix[j, i]
}
}
}
return(matrix)
}
# Compute the Cramér's V matrix
cramers_v <- cramers_v_matrix(categorical_data)
# Melt the matrix for ggplot
melted_cramers_v <- melt(cramers_v, na.rm = TRUE)
# Generate the heatmap
ggplot(data = melted_cramers_v, aes(x = Var1, y = Var2, fill = value)) +
geom_tile() + # Add white borders for tiles
geom_text(aes(label = round(value, 2)), size = 6) + # Add Cramér's V values
scale_fill_gradient2(
low = colors[2],
high = colors[1],
mid = colors[3],
midpoint = 0.5,
limits = c(0, 1),
guide = guide_colorbar(
title = "",
barwidth = 30, # Adjust color bar width
barheight = 1 # Adjust color bar height
)
) +
theme_rose_pine(base_size = 14) + # Apply Rosé Pine theme
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 16),
axis.text.y = element_text(size = 16),
plot.title = element_text(size = 20, face = "bold", hjust = 0.5), # Center the title
axis.title = element_blank(), # Remove axis titles
panel.grid = element_blank(), # Remove grid lines
legend.position = "top",
legend.text = element_text(size = 12), # Adjust legend text size
legend.title = element_text(size = 14, face = "bold")
) +
ggtitle("Cramér's V Correlation Matrix for Categorical Features") # Add plot title

ggpairs(
data = cleaned_df_columnwise[, selected_cols],
mapping = aes(color = "Total_Profit"), # Replace with a relevant column if needed
lower = list(
continuous = wrap("smooth", color = colors[2], size = 0.8),
combo = wrap("facetdensity", alpha = 0.7, fill = colors[3])
),
upper = list(
continuous = wrap("cor", size = 6, color = rose_pine_colors$text)
),
diag = list(
continuous = wrap("densityDiag", fill = colors[1], alpha = 0.7)
)
) +
theme_rose_pine(base_size = 12) + # Apply the Rosé Pine theme
theme(
plot.title = element_text(size = 30, face = "bold"), # Title font size
axis.text = element_text(size = 22), # Axis text size
axis.title = element_text(size = 22), # Axis title size
strip.text = element_text(size = 14) # Size of facet labels
) +
labs(
title = "Pairplot with Correlation"
)

# Parse Registration_Date as datetime
cleaned_df_columnwise$Registration_Date <- mdy_hm(cleaned_df_columnwise$Registration_Date)
# Extract the month and year
cleaned_df_columnwise <- cleaned_df_columnwise %>%
mutate(Month = month(Registration_Date, label = TRUE, abbr = TRUE)) # e.g., Jan, Feb
monthly_profit <- cleaned_df_columnwise %>%
group_by(Month) %>%
summarise(Total_Profit = sum(Total_Profit, na.rm = TRUE)) %>%
arrange(Month)
ggplot(monthly_profit, aes(x = Month, y = Total_Profit, group = 1)) +
geom_line(color = colors[1], size = 1.2, linetype="twodash") + # Line with custom color
geom_point(color = colors[2], size = 3) + # Points on the line
labs(
title = "Monthly Total Profit in 2013",
x = "Month",
y = "Total Profit"
) +
theme_rose_pine(base_size = 15) +
theme(
plot.title = element_text(hjust = 0.5, face = "bold"), # Centered title
axis.text.x = element_text(size = 14),
axis.text.y = element_text(size = 14)
)

# Group data by Marketing_Channel_Type and Month, then calculate the total profit
monthly_profit_by_channel <- cleaned_df_columnwise %>%
group_by(Month, Marketing_Channel_Type) %>%
summarise(Total_Profit = sum(Total_Profit, na.rm = TRUE)) %>%
arrange(Month)
# Check the unique marketing channels and colors
unique_channels <- unique(monthly_profit_by_channel$Marketing_Channel_Type)
# Ensure the colors match the unique channels
colors_for_channels <- setNames(colors[1:length(unique_channels)], unique_channels)
# Plot with color mapping
ggplot(monthly_profit_by_channel, aes(x = Month, y = Total_Profit, color = Marketing_Channel_Type, group = Marketing_Channel_Type)) +
geom_line(size = 1.2, linetype="twodash") + # Line for each marketing channel
geom_point(size = 3) + # Points on the lines
scale_color_manual(values = colors_for_channels) + # Map colors to channels
labs(
title = "Monthly Total Profit by Marketing Channel in 2013",
x = "Month",
y = "Total Profit",
color = "Marketing Channel" # Legend title
) +
theme_rose_pine(base_size = 15) + # Apply Rosé Pine theme
theme(
plot.title = element_text(hjust = 0.5, face = "bold"), # Centered title
axis.text.x = element_text(size = 16),
axis.text.y = element_text(size = 16),
legend.position = "right", # Legend position
legend.text = element_text(size = 14), # Legend text size
legend.title = element_text(size = 16, face = "bold") # Legend title size
)

LS0tCnRpdGxlOiAiQ3VzdG9tZXIgUHJvZml0YWJpbGl0eSBhbmQgTWFya2V0aW5nIEFuYWx5c2lzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY3NzOiAuLi9hc3NldHMvY3NzL3N0eWxlcy5jc3MKICAgIGluY2x1ZGVzOgogICAgICBiZWZvcmVfYm9keTogLi4vdGVtcGxhdGVzL2dpdGh1Yi5odG1sCiAgICAgIGFmdGVyX2JvZHk6IC4uL3RlbXBsYXRlcy9mb290ZXIuaHRtbAplZGl0b3Jfb3B0aW9uczogIAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgMS4gSW50cm9kdWN0aW9uCgpJbiB0b2RheSdzIGNvbXBldGl0aXZlIG1hcmtldCwgdW5kZXJzdGFuZGluZyBjdXN0b21lciBiZWhhdmlvciBpcyBjcnVjaWFsIGZvciBkcml2aW5nIHByb2ZpdGFiaWxpdHkgYW5kIGxvbmctdGVybSBzdWNjZXNzLiBCeSBhbmFseXppbmcgdHJhbnNhY3Rpb24gZGF0YSwgYnVzaW5lc3NlcyBjYW4gZ2FpbiBpbnNpZ2h0cyBpbnRvIGtleSBmYWN0b3JzIHN1Y2ggYXMgKipjdXN0b21lciBkZW1vZ3JhcGhpY3MqKiwgKiptYXJrZXRpbmcgY2hhbm5lbCBwZXJmb3JtYW5jZSoqLCBhbmQgKipwcm9maXRhYmlsaXR5IHRyZW5kcyoqLiBUaGVzZSBpbnNpZ2h0cyBlbmFibGUgYnVzaW5lc3NlcyB0byBtYWtlIGRhdGEtZHJpdmVuIGRlY2lzaW9ucyB0byBlbmhhbmNlIGN1c3RvbWVyIHJldGVudGlvbiwgb3B0aW1pemUgbWFya2V0aW5nIHN0cmF0ZWdpZXMsIGFuZCBpbmNyZWFzZSBvdmVyYWxsIHJldmVudWUuCgpUaGlzIHByb2plY3QgbGV2ZXJhZ2VzIGEgY29tcHJlaGVuc2l2ZSBkYXRhc2V0IHRvIGV4cGxvcmUgdGhlc2UgZHluYW1pY3MsIGZvY3VzaW5nIG9uIGFjdGlvbmFibGUgb3V0Y29tZXMgYW5kIHJlY29tbWVuZGF0aW9ucy4KCi0tLQoKIyMgRGF0YXNldCBDb2x1bW4gSW5mb3JtYXRpb24KCkJlbG93IGlzIHRoZSBkZXNjcmlwdGlvbiBvZiBlYWNoIGNvbHVtbiBpbiB0aGUgZGF0YXNldDoKCnwgU04gIHwgQ29sdW1uIE5hbWUgICAgICAgICAgICAgICAgICAgIHwgQ29sdW1uIERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IDEgICB8IEN1c3RvbWVyX0lEICAgICAgICAgICAgICAgICAgICB8IFVuaXF1ZSBpZGVudGlmaWVyIGZvciBlYWNoIGN1c3RvbWVyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAyICAgfCBHZW5kZXIgICAgICAgICAgICAgICAgICAgICAgICAgIHwgR2VuZGVyIG9mIHRoZSBjdXN0b21lciAoTWFsZS9GZW1hbGUpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgMyAgIHwgRGF0ZV9PZl9CaXJ0aCAgICAgICAgICAgICAgICAgICB8IEJpcnRoIGRhdGUgb2YgdGhlIGN1c3RvbWVyIGluIE1NL0REL1lZWVkgZm9ybWF0ICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDQgICB8IExvY2F0aW9uICAgICAgICAgICAgICAgICAgICAgICAgfCBMb2NhdGlvbiAoY2l0eSkgb2YgdGhlIGN1c3RvbWVyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCA1ICAgfCBDb250YWN0X0FsbG93ZWQgICAgICAgICAgICAgICAgIHwgV2hldGhlciB0aGUgY3VzdG9tZXIgaGFzIGFsbG93ZWQgdG8gYmUgY29udGFjdGVkIChZL04pICAgICAgICAgICAgICAgICAgICAgfAp8IDYgICB8IFJlZ2lzdHJhdGlvbl9EYXRlICAgICAgICAgICAgICAgfCBEYXRlIGFuZCB0aW1lIHdoZW4gdGhlIGN1c3RvbWVyIHJlZ2lzdGVyZWQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCA3ICAgfCBNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlICAgICAgICAgIHwgVGhlIG1hcmtldGluZyBjaGFubmVsIHRocm91Z2ggd2hpY2ggdGhlIGN1c3RvbWVyIHdhcyBhY3F1aXJlZCAgICAgICAgICAgICB8CnwgOCAgIHwgRmlyc3RfT3JkZXJfUHJvZml0ICAgICAgICAgICAgICB8IFByb2ZpdCBmcm9tIHRoZSBjdXN0b21lcuKAmXMgZmlyc3Qgb3JkZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgOSAgIHwgU3Vic2VxdWVudF9PcmRlcl9Qcm9maXQgICAgICAgICB8IFByb2ZpdCBmcm9tIGFsbCBzdWJzZXF1ZW50IG9yZGVycyBvZiB0aGUgY3VzdG9tZXIgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDEwICB8IFN1YnNlcXVlbnRfT3JkZXJzX0NvdW50ICAgICAgICAgfCBOdW1iZXIgb2Ygc3Vic2VxdWVudCBvcmRlcnMgcGxhY2VkIGJ5IHRoZSBjdXN0b21lciAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAxMSAgfCBUb3RhbF92YWx1ZV9vZl9hbGxfcHJvbW90aW9ucyAgIHwgVG90YWwgdmFsdWUgb2YgYWxsIHByb21vdGlvbnMgdGhlIGN1c3RvbWVyIHJlY2VpdmVkICAgICAgICAgICAgICAgICAgICAgICB8CnwgMTIgIHwgQWdlICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEFnZSBvZiB0aGUgY3VzdG9tZXIgKGNhbGN1bGF0ZWQgZnJvbSBEYXRlX09mX0JpcnRoKSAgICAgICAgICAgICAgICAgICAgICAgfAp8IDEzICB8IFRvdGFsX1Byb2ZpdCAgICAgICAgICAgICAgICAgICAgfCBUb3RhbCBwcm9maXQgZ2VuZXJhdGVkIGZyb20gdGhlIGN1c3RvbWVyIChGaXJzdCArIFN1YnNlcXVlbnQpICAgICAgICAgICAgIHwKCi0tLQoKIyMgR29hbAoKVGhlIHByaW1hcnkgZ29hbCBvZiB0aGlzIHByb2plY3QgaXMgdG8gYW5hbHl6ZSAqKmN1c3RvbWVyIGJlaGF2aW9yKiogYW5kICoqcHJvZml0YWJpbGl0eSoqIGluIHJlbGF0aW9uIHRvIHZhcmlvdXMgbWFya2V0aW5nIGNoYW5uZWxzLiBCeSBsZXZlcmFnaW5nIHRoZSBkYXRhc2V0LCB3aGljaCBpbmNsdWRlcyBlc3NlbnRpYWwgY3VzdG9tZXIgYW5kIHRyYW5zYWN0aW9uIGRldGFpbHMsIHdlIGFpbSB0byB1bmNvdmVyIGZhY3RvcnMgdGhhdCBkcml2ZSAqKkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIChDTFYpKiogYW5kIG1lYXN1cmUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgZGlmZmVyZW50IG1hcmtldGluZyBzdHJhdGVnaWVzLgoKVGhyb3VnaCB0aGlzIGFuYWx5c2lzLCB0aGUgYnVzaW5lc3Mgd2lsbCBnYWluIGFjdGlvbmFibGUgaW5zaWdodHMgdG86Ci0gT3B0aW1pemUgbWFya2V0aW5nIGVmZm9ydHMuCi0gSW1wcm92ZSBjdXN0b21lciByZXRlbnRpb24uCi0gSW5jcmVhc2Ugb3ZlcmFsbCBwcm9maXRzLgoKLS0tCgojIyBEZWxpdmVyYWJsZXMKCiMjIyAxLiAqKkRhdGEgQ2xlYW5pbmcgYW5kIFRyYW5zZm9ybWF0aW9uKioKLSBDbGVhbiBhbmQgcHJlcHJvY2VzcyB0aGUgZGF0YXNldCBieToKICAtIEhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzLgogIC0gRGV0ZWN0aW5nIGFuZCBhZGRyZXNzaW5nIG91dGxpZXJzLgogIC0gQ29udmVydGluZyBjb2x1bW5zIHRvIGFwcHJvcHJpYXRlIGZvcm1hdHMgZm9yIGFuYWx5c2lzLgoKLS0tCgojIyMgMi4gKipFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpKioKLSBQZXJmb3JtIHN0YXRpc3RpY2FsIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9ucyB0bzoKICAtIElkZW50aWZ5IHRyZW5kcywgcmVsYXRpb25zaGlwcywgYW5kIHBhdHRlcm5zLgogIC0gSGlnaGxpZ2h0IGFub21hbGllcyBvciB1bnVzdWFsIGJlaGF2aW9ycyBpbiB0aGUgZGF0YS4KICAKIyMjIDMuICoqT3V0bGllciBEZXRlY3Rpb24qKgotICoqUHVycG9zZSoqOiBEZXRlY3QgdW51c3VhbCBwYXR0ZXJucyBpbiBjdXN0b21lciB0cmFuc2FjdGlvbnMsIHN1Y2ggYXM6CiAgLSAqKkV4Y2VwdGlvbmFsbHkgaGlnaCBwcm9maXRzKioKICAtICoqUHJvbW90aW9ucyBvciBkaXNjb3VudHMqKgogIC0gKipVbnVzdWFsIG9yZGVyIGJlaGF2aW9yKioKLSAqKkdvYWwqKjogVW5kZXJzdGFuZCBleGNlcHRpb25hbCBjYXNlcywgcmVmaW5lIG1hcmtldGluZyBzdHJhdGVnaWVzLCBhbmQgbWFuYWdlIGJvdGggaGlnaC12YWx1ZSBhbmQgbG93LXZhbHVlIGN1c3RvbWVycyBlZmZlY3RpdmVseS4KCi0tLQoKIyMgU3VtbWFyeQoKVGhpcyBhbmFseXNpcyB3aWxsIGVtcG93ZXIgdGhlIGJ1c2luZXNzIHRvOgotIFVuZGVyc3RhbmQgaXRzIGN1c3RvbWVyIGJhc2UgbW9yZSBkZWVwbHkuCi0gVGFpbG9yIG1hcmtldGluZyBzdHJhdGVnaWVzIHRvIG1heGltaXplIGVuZ2FnZW1lbnQgYW5kIHByb2ZpdGFiaWxpdHkuCi0gUmVmaW5lIGNhbXBhaWducyB0byBpbXByb3ZlICoqY3VzdG9tZXIgc2F0aXNmYWN0aW9uKiogYW5kICoqbG9uZy10ZXJtIGdyb3d0aCoqLgoKQnkgZm9jdXNpbmcgb24gdGhlc2Ugb2JqZWN0aXZlcywgdGhlIGJ1c2luZXNzIGNhbiB0dXJuIGRhdGEtZHJpdmVuIGluc2lnaHRzIGludG8gaW1wYWN0ZnVsIGFjdGlvbnMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIExvYWQgcmVxdWlyZWQgbGlicmFyaWVzCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShnZ3RoZW1lcykgIyBGb3IgY3VzdG9tIHRoZW1lcwpsaWJyYXJ5KGxzcikgIyBGb3IgQ3JhbcOpcidzIFYKbGlicmFyeShyb3NlcGluZVRoZW1lKQoKIyBFeHRyYWN0IHRoZSBmaXJzdCBuIGFjY2VudHMgZnJvbSByb3NlX3BpbmVfY29sb3JzCmdldF9yb3NlX3BpbmVfY29sb3JzIDwtIGZ1bmN0aW9uKG4pIHsKICBhY2NlbnRzIDwtIGdyZXAoImFjY2VudCIsIG5hbWVzKHJvc2VfcGluZV9jb2xvcnMpLCB2YWx1ZSA9IFRSVUUpICMgR2V0IGFsbCBhY2NlbnQga2V5cwogIHVubGlzdChyb3NlX3BpbmVfY29sb3JzW2FjY2VudHNdKVsxOm5dICAjIEV4dHJhY3QgZmlyc3QgbiBhY2NlbnQgdmFsdWVzCn0KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgMi4gRGF0YSBFeHBsb3JhdGlvbiBhbmQgUHJlcHJvY2Vzc2luZwoKCiMjIExvYWRpbmcgYW5kIFByZXZpZXdpbmcgdGhlIERhdGEKCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBsb2FkIHRoZSBkYXRhc2V0IGZyb20gYSBDU1YgZmlsZSBhbmQgcHJldmlldyB0aGUgZmlyc3QgZmV3IHJvd3MuCgpgYGB7cn0KIyBMb2FkIHRoZSBDU1YgZmlsZQpkZiA8LSByZWFkLmNzdigiLi4vZGF0YS9tYXJrZXRpbmdfZGF0YS5jc3YiKQpgYGAKCioqRGlzcGxheWluZyB0aGUgRmlyc3QgRmV3IFJvd3Mgb2YgdGhlIERhdGFzZXQqKgoKYGBge3J9CiMgRGlzcGxheSB0aGUgZmlyc3QgZmV3IHJvd3MKaGVhZChkZikKYGBgCgojIyBEYXRhc2V0IE92ZXJ2aWV3CgotLS0KCiMjIyBOdW1iZXIgb2YgUm93cyBhbmQgQ29sdW1ucwpgYGB7cn0KIyBUb3RhbCBudW1iZXIgb2Ygcm93cwpudW1fcm93cyA8LSBkaW0oZGYpWzFdCgojIFRvdGFsIG51bWJlciBvZiBjb2x1bW5zCm51bV9jb2x1bW5zIDwtIGRpbShkZilbMl0KCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJOdW1iZXIgb2Ygcm93czoiLCBudW1fcm93cywgIlxuIikKY2F0KCJOdW1iZXIgb2YgY29sdW1uczoiLCBudW1fY29sdW1ucywgIlxuIikKYGBgCgojIyMgQ2hlY2tpbmcgQ29sdW1uIE5hbWVzCmBgYHtyfQojIEdldCBjb2x1bW4gbmFtZXMKYXMuZGF0YS5mcmFtZShjb2xuYW1lcyhkZikpCmBgYAoKIyMjIENoZWNraW5nIGZvciBOQSB2YWx1ZXMgYW5kIEhhbmRsaW5nIE1pc3NpbmcgRGF0YQpgYGB7cn0KYXMuZGF0YS5mcmFtZShjb2xTdW1zKGlzLm5hKGRmKSkpCmBgYAoKIyMjIElkZW50aWZ5aW5nIGFuZCBSZW1vdmluZyBEdXBsaWNhdGVzCmBgYHtyfQojIEZpbmQgZHVwbGljYXRlIHJvd3MKZGZbZHVwbGljYXRlZChkZiksIF0KYGBgCgojIyMgRGlzcGxheWluZyB0aGUgRGF0YSBTdHJ1Y3R1cmUKYGBge3J9CiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldApzdHIoZGYpCmBgYAoKIyMjIEdlbmVyYXRpbmcgU3VtbWFyeSBvZiBEYXRhCmBgYHtyfQojIEdldCBhIHN0YXRpc3RpY2FsIHN1bW1hcnkgb2YgbnVtZXJpY2FsIGNvbHVtbnMKc3VtbWFyeShkZikKYGBgCgojIDMuIEV4cGxhdG9yeSBEYXRhIEFuYWx5c2lzCgotLS0KCiMjIEhpc3RvZ3JhbSBBbmFseXNpcwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIHRoZSBoaXN0b2dyYW1zIGZvciB0aGUgZm9sbG93aW5nIHNpeCBrZXkgdmFyaWFibGVzIGZyb20gdGhlIGRhdGFzZXQ6CgotICoqRmlyc3QgT3JkZXIgUHJvZml0KioKLSAqKlN1YnNlcXVlbnQgT3JkZXIgUHJvZml0KioKLSAqKlN1YnNlcXVlbnQgT3JkZXJzIENvdW50KioKLSAqKlRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKioKLSAqKkFnZSoqCi0gKipUb3RhbCBQcm9maXQqKgoKVGhlc2UgaGlzdG9ncmFtcyBwcm92aWRlIGluc2lnaHRzIGludG8gdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIGRhdGEsIGhlbHBpbmcgdXMgaWRlbnRpZnkgc2tld25lc3MsIHRoZSBwcmVzZW5jZSBvZiBvdXRsaWVycywgYW5kIHBvdGVudGlhbCBkYXRhIHRyYW5zZm9ybWF0aW9ucy4KCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTB9CiMgU2VsZWN0IHNwZWNpZmljIGNvbHVtbnMKc2VsZWN0ZWRfY29scyA8LSBjKAogICJGaXJzdF9PcmRlcl9Qcm9maXQiLCAiU3Vic2VxdWVudF9PcmRlcl9Qcm9maXQiLCAiU3Vic2VxdWVudF9PcmRlcnNfQ291bnQiLAogICJUb3RhbF92YWx1ZV9vZl9hbGxfcHJvbW90aW9ucyIsICJBZ2UiLCAiVG90YWxfUHJvZml0IgopCgojIEF1dG9tYXRpY2FsbHkgZXh0cmFjdCB0aGUgcmVxdWlyZWQgbnVtYmVyIG9mIGNvbG9ycwpjb2xvcnMgPC0gZ2V0X3Jvc2VfcGluZV9jb2xvcnMobGVuZ3RoKHNlbGVjdGVkX2NvbHMpKSAKCiMgUHJlcGFyZSB0aGUgZGF0YSBsaXN0CmRhdGFfbGlzdCA8LSBsYXBwbHkoc2VsZWN0ZWRfY29scywgZnVuY3Rpb24oY29sKSBkZltbY29sXV0pCm5hbWVzKGRhdGFfbGlzdCkgPC0gc2VsZWN0ZWRfY29scwoKIyBHZW5lcmF0ZSBIaXN0b2dyYW1zIER5bmFtaWNhbGx5Cmhpc3RvZ3JhbXMgPC0gbGFwcGx5KHNlcV9hbG9uZyhkYXRhX2xpc3QpLCBmdW5jdGlvbihpKSB7CiAgZm9ybWF0dGVkX3RpdGxlIDwtIHRvb2xzOjp0b1RpdGxlQ2FzZShnc3ViKCJfIiwgIiAiLCBuYW1lcyhkYXRhX2xpc3QpW2ldKSkgICMgRm9ybWF0IGNvbHVtbiBuYW1lCiAgCiAgZ2dwbG90KGRhdGEuZnJhbWUodmFsdWUgPSBkYXRhX2xpc3RbW2ldXSksIGFlcyh4ID0gdmFsdWUpKSArCiAgICBnZW9tX2hpc3RvZ3JhbV9kZWZhdWx0KGRhdGFfbGVuZ3RoID0gbGVuZ3RoKGRhdGFfbGlzdFtbaV1dKSwgZmlsbF9jb2xvciA9IGNvbG9yc1tpXSkgKyAgIyBEeW5hbWljIGJpbnMgJiBjb2xvcgogICAgbGFicygKICAgICAgdGl0bGUgPSBwYXN0ZSgiIiwgZm9ybWF0dGVkX3RpdGxlKSwgICMgVXNlIGZvcm1hdHRlZCB0aXRsZQogICAgICB4ID0gIiIsCiAgICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArIAogICAgdGhlbWVfcm9zZV9waW5lKCkKfSkKCiMgQ29tYmluZSBBbGwgSGlzdG9ncmFtcyBpbnRvIGEgR3JpZCBMYXlvdXQKZG8uY2FsbChncmlkLmFycmFuZ2UsIGMoaGlzdG9ncmFtcywgbmNvbCA9IDMpKQpgYGAKIyMjIE9ic2VydmF0aW9ucyBhbmQgUmVjb21tZW5kYXRpb25zCgojIyMjICoqMS4gRmlyc3QgT3JkZXIgUHJvZml0KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBIaWdobHkgcmlnaHQtc2tld2VkIGRpc3RyaWJ1dGlvbiB3aXRoIG1vc3QgdmFsdWVzIG5lYXIgemVyby4KICAtIEEgbG9uZyB0YWlsIHN1Z2dlc3RzIGhpZ2gtcHJvZml0IG91dGxpZXJzLgotICoqUmVjb21tZW5kYXRpb24qKjoKICAtIEFwcGx5IGEgbG9nYXJpdGhtaWMgdHJhbnNmb3JtYXRpb24gdG8gcmVkdWNlIHNrZXduZXNzIGZvciBhbmFseXNpcy4KICAtIEludmVzdGlnYXRlIG91dGxpZXJzIHRvIGRldGVybWluZSB0aGVpciB2YWxpZGl0eSBvciBjb25zaWRlciBjYXBwaW5nIHRoZW0uCgotLS0KCiMjIyMgKioyLiBTdWJzZXF1ZW50IE9yZGVyIFByb2ZpdCoqCi0gKipPYnNlcnZhdGlvbioqOgogIC0gUmlnaHQtc2tld2VkIHdpdGggbW9zdCB2YWx1ZXMgbmVhciB6ZXJvIGFuZCBhIGZldyB2ZXJ5IGhpZ2ggcHJvZml0cy4KICAtIFRoZSBkaXN0cmlidXRpb24gaXMgc2ltaWxhciB0byB0aGUgRmlyc3QgT3JkZXIgUHJvZml0LgotICoqUmVjb21tZW5kYXRpb24qKjoKICAtIFVzZSBzaW1pbGFyIHRyZWF0bWVudCBhcyBGaXJzdCBPcmRlciBQcm9maXQgKGUuZy4sIGxvZ2FyaXRobWljIHRyYW5zZm9ybWF0aW9uIG9yIG91dGxpZXIgY2FwcGluZykuCiAgLSBFeHBsb3JlIGN1c3RvbWVyIHNlZ21lbnRzIHRoYXQgZ2VuZXJhdGUgaGlnaCBzdWJzZXF1ZW50IHByb2ZpdHMuCgotLS0KCiMjIyMgKiozLiBTdWJzZXF1ZW50IE9yZGVycyBDb3VudCoqCi0gKipPYnNlcnZhdGlvbioqOgogIC0gTWFqb3JpdHkgb2YgY3VzdG9tZXJzIHBsYWNlZCB2ZXJ5IGZldyBzdWJzZXF1ZW50IG9yZGVycyAoKiriiaQgNSoqKS4KICAtIEEgc2hhcnAgZGVjbGluZSBpbiBjdXN0b21lciBjb3VudCBhcyB0aGUgbnVtYmVyIG9mIG9yZGVycyBpbmNyZWFzZXMuCi0gKipSZWNvbW1lbmRhdGlvbioqOgogIC0gR3JvdXAgaGlnaGVyIG9yZGVyIGNvdW50cyBpbnRvIGJpbnMgZm9yIGJldHRlciB2aXN1YWwgaW50ZXJwcmV0YWJpbGl0eS4KICAtIEludmVzdGlnYXRlIGZhY3RvcnMgaW5mbHVlbmNpbmcgY3VzdG9tZXJzIHdpdGggaGlnaGVyIG9yZGVyIGNvdW50cyAoZS5nLiwgYWdlLCBwcm9tb3Rpb25zKS4KCi0tLQoKIyMjIyAqKjQuIFRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBIaWdobHkgc2tld2VkIHdpdGggbW9zdCBjdXN0b21lcnMgcmVjZWl2aW5nIHByb21vdGlvbnMgb2YgbG93IHZhbHVlLgogIC0gRmV3IGN1c3RvbWVycyByZWNlaXZlZCBzaWduaWZpY2FudGx5IGhpZ2gtdmFsdWUgcHJvbW90aW9ucywgY3JlYXRpbmcgYSBsb25nIHRhaWwuCi0gKipSZWNvbW1lbmRhdGlvbioqOgogIC0gSW52ZXN0aWdhdGUgd2hldGhlciBoaWdoIHByb21vdGlvbiB2YWx1ZXMgY29ycmVsYXRlIHdpdGggaGlnaCBwcm9maXQgb3Igb3JkZXIgY291bnRzLgogIC0gQ29uc2lkZXIgZ3JvdXBpbmcgcHJvbW90aW9uIHZhbHVlcyBpbnRvIGJpbnMgZm9yIGJldHRlciBpbnRlcnByZXRhYmlsaXR5LgoKLS0tCgojIyMjICoqNS4gQWdlKioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBBcHByb3hpbWF0ZWx5IGJlbGwtc2hhcGVkLCB3aXRoIG1vc3QgY3VzdG9tZXJzIGZhbGxpbmcgYmV0d2VlbiAyMOKAkzUwIHllYXJzIG9mIGFnZS4KICAtIEZld2VyIGN1c3RvbWVycyBhcmUgeW91bmdlciB0aGFuIDIwIG9yIG9sZGVyIHRoYW4gNTAuCi0gKipSZWNvbW1lbmRhdGlvbioqOgogIC0gTm8gaW1tZWRpYXRlIGFjdGlvbiByZXF1aXJlZDsgdGhlIGRpc3RyaWJ1dGlvbiBpcyBiYWxhbmNlZCBhbmQgY2FuIGJlIHVzZWQgYXMtaXMgZm9yIG1vZGVsaW5nLgoKLS0tCgojIyMjICoqNi4gVG90YWwgUHJvZml0KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBTaW1pbGFyIHRvIHByb2ZpdCB2YXJpYWJsZXMsIHRoZSBkaXN0cmlidXRpb24gaXMgcmlnaHQtc2tld2VkIHdpdGggbW9zdCB2YWx1ZXMgY29uY2VudHJhdGVkIG5lYXIgemVyby4KICAtIEhpZ2gtcHJvZml0IG91dGxpZXJzIGFyZSBwcmVzZW50LgotICoqUmVjb21tZW5kYXRpb24qKjoKICAtIEludmVzdGlnYXRlIGhpZ2gtcHJvZml0IGN1c3RvbWVycyBhbmQgdGhlaXIgY2hhcmFjdGVyaXN0aWNzLgogIC0gQ29uc2lkZXIgdHJhbnNmb3JtYXRpb25zIG9yIG91dGxpZXIgaGFuZGxpbmcgZm9yIG1vZGVsaW5nIHB1cnBvc2VzLgogIApUaGlzIGFuYWx5c2lzIGhlbHBzIGlkZW50aWZ5IHBvdGVudGlhbCBwcmVwcm9jZXNzaW5nIHN0ZXBzIGFuZCBndWlkZXMgZXhwbG9yYXRvcnkgYW5hbHlzaXMgdG8gYmV0dGVyIHVuZGVyc3RhbmQgY3VzdG9tZXIgYmVoYXZpb3IuCgotLS0KCiMjIERlbnNpdHkgUGxvdCBBbmFseXNpcwoKVGhpcyBzZWN0aW9uIHByb3ZpZGVzIG9ic2VydmF0aW9ucyBhbmQgcmVjb21tZW5kYXRpb25zIGJhc2VkIG9uIHRoZSBkZW5zaXR5IHBsb3RzIG9mIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOgoKLSAqKkZpcnN0IE9yZGVyIFByb2ZpdCoqCi0gKipTdWJzZXF1ZW50IE9yZGVyIFByb2ZpdCoqCi0gKipTdWJzZXF1ZW50IE9yZGVycyBDb3VudCoqCi0gKipUb3RhbCBWYWx1ZSBvZiBBbGwgUHJvbW90aW9ucyoqCi0gKipBZ2UqKgotICoqVG90YWwgUHJvZml0KioKCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTB9CiMgR2VuZXJhdGUgS0RFIHBsb3RzIGR5bmFtaWNhbGx5CmtkZSA8LSBsYXBwbHkoc2VxX2Fsb25nKGRhdGFfbGlzdCksIGZ1bmN0aW9uKGkpIHsKICBmb3JtYXR0ZWRfdGl0bGUgPC0gdG9vbHM6OnRvVGl0bGVDYXNlKGdzdWIoIl8iLCAiICIsIG5hbWVzKGRhdGFfbGlzdClbaV0pKQogIAogIGdncGxvdChkYXRhLmZyYW1lKHZhbHVlID0gZGF0YV9saXN0W1tpXV0pLCBhZXMoeCA9IHZhbHVlKSkgKwogICAgZ2VvbV9kZW5zaXR5X2RlZmF1bHQoZmlsbF9jb2xvciA9IGNvbG9yc1tpXSkgKyAgIyBEeW5hbWljIGNvbG9yIGFzc2lnbm1lbnQKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gcGFzdGUoIiIsIGZvcm1hdHRlZF90aXRsZSksCiAgICAgIHggPSAiIiwKICAgICAgeSA9ICJEZW5zaXR5IgogICAgKSArCiAgICB0aGVtZV9yb3NlX3BpbmUoKQp9KQoKIyBDb21iaW5lIGFsbCBwbG90cyBpbnRvIGEgZ3JpZCBsYXlvdXQKZG8uY2FsbChncmlkLmFycmFuZ2UsIGMoa2RlLCBuY29sID0gMykpCmBgYAojIyMgT2JzZXJ2YXRpb25zIGFuZCBSZWNvbW1lbmRhdGlvbnMKCiMjIyMgKioxLiBGaXJzdCBPcmRlciBQcm9maXQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFJpZ2h0LXNrZXdlZCBkaXN0cmlidXRpb24gd2l0aCBhIHNoYXJwIHBlYWsgYXQgbG93IHByb2ZpdCB2YWx1ZXMuCiAgLSBMb25nIHRhaWwgZHVlIHRvIGhpZ2gtcHJvZml0IGN1c3RvbWVycy4KLSAqKlJlY29tbWVuZGF0aW9uKio6CiAgLSBBcHBseSBhIGxvZ2FyaXRobWljIHRyYW5zZm9ybWF0aW9uIGZvciBza2V3bmVzcyBjb3JyZWN0aW9uLgogIC0gRXhhbWluZSBvdXRsaWVycyBmb3IgdmFsaWRpdHkgb3IgYXBwbHkgY2FwcGluZyB0byByZWR1Y2UgaW5mbHVlbmNlLgoKLS0tCgojIyMjICoqMi4gU3Vic2VxdWVudCBPcmRlciBQcm9maXQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFNoYXJwIHBlYWsgYXQgbG93IHZhbHVlcywgd2l0aCBhIHNpbWlsYXIgcmlnaHQtc2tld2VkIHRyZW5kIGFzIEZpcnN0IE9yZGVyIFByb2ZpdC4KICAtIExvbmcgdGFpbCBleHRlbmRpbmcgdG93YXJkcyB2ZXJ5IGhpZ2ggdmFsdWVzLgotICoqUmVjb21tZW5kYXRpb24qKjoKICAtIENvbnNpZGVyIGhhbmRsaW5nIG91dGxpZXJzIHRocm91Z2ggY2FwcGluZyBvciBXaW5zb3JpemF0aW9uLgogIC0gSW52ZXN0aWdhdGUgY3VzdG9tZXIgc2VnbWVudHMgY29udHJpYnV0aW5nIHRvIGhpZ2ggc3Vic2VxdWVudCBwcm9maXRzLgoKLS0tCgojIyMjICoqMy4gU3Vic2VxdWVudCBPcmRlcnMgQ291bnQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIENsZWFyIHBlYWsgYXQgMeKAkzIgb3JkZXJzLCBpbmRpY2F0aW5nIG1vc3QgY3VzdG9tZXJzIG1hZGUgZmV3IHJlcGVhdCBwdXJjaGFzZXMuCiAgLSBEaXN0cmlidXRpb24gZmxhdHRlbnMgc2lnbmlmaWNhbnRseSBmb3IgaGlnaGVyIG9yZGVyIGNvdW50cy4KLSAqKlJlY29tbWVuZGF0aW9uKio6CiAgLSBHcm91cCBoaWdoZXIgY291bnRzIGludG8gYmlucyBmb3IgY2xlYXJlciBpbnNpZ2h0cy4KICAtIEV4cGxvcmUgZmFjdG9ycyAoZS5nLiwgZGVtb2dyYXBoaWNzLCBwcm9tb3Rpb25zKSBpbmZsdWVuY2luZyBjdXN0b21lcnMgd2l0aCBtYW55IG9yZGVycy4KCi0tLQoKIyMjIyAqKjQuIFRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBIaWdobHkgc2tld2VkLCB3aXRoIG1vc3QgdmFsdWVzIGNvbmNlbnRyYXRlZCBuZWFyIHplcm8uCiAgLSBMb25nIHRhaWwgc3VnZ2VzdHMgYSBmZXcgY3VzdG9tZXJzIHJlY2VpdmVkIHNpZ25pZmljYW50bHkgaGlnaCBwcm9tb3Rpb24gdmFsdWVzLgotICoqUmVjb21tZW5kYXRpb24qKjoKICAtIEFzc2VzcyB3aGV0aGVyIGhpZ2ggcHJvbW90aW9uIHZhbHVlcyB0cmFuc2xhdGUgdG8gaGlnaGVyIHByb2ZpdHMgb3Igb3JkZXIgY291bnRzLgogIC0gQ29uc2lkZXIgc2VnbWVudGluZyBjdXN0b21lcnMgYmFzZWQgb24gcHJvbW90aW9uIGxldmVscyBmb3IgZnVydGhlciBhbmFseXNpcy4KCi0tLQoKIyMjIyAqKjUuIEFnZSoqCi0gKipPYnNlcnZhdGlvbioqOgogIC0gRGlzdHJpYnV0aW9uIGlzIGFwcHJveGltYXRlbHkgYmVsbC1zaGFwZWQsIGNlbnRlcmVkIGFyb3VuZCAzMOKAkzQwIHllYXJzLgogIC0gU2tld25lc3MgaXMgbWluaW1hbCwgd2l0aCBiYWxhbmNlZCBkYXRhIGZvciBtb3N0IGFnZSByYW5nZXMuCi0gKipSZWNvbW1lbmRhdGlvbioqOgogIC0gTm8gdHJhbnNmb3JtYXRpb24gbmVlZGVkLiBUaGUgdmFyaWFibGUgY2FuIGJlIHVzZWQgYXMtaXMgaW4gYW5hbHlzaXMuCgotLS0KCiMjIyMgKio2LiBUb3RhbCBQcm9maXQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFNpbWlsYXIgcmlnaHQtc2tld2VkIHRyZW5kIGFzIG90aGVyIHByb2ZpdCB2YXJpYWJsZXMsIHdpdGggYSBwZWFrIGF0IGxvdyB2YWx1ZXMuCiAgLSBMb25nIHRhaWwgc2hvd3MgdGhlIHByZXNlbmNlIG9mIGEgZmV3IGhpZ2gtcHJvZml0IGN1c3RvbWVycy4KLSAqKlJlY29tbWVuZGF0aW9uKio6CiAgLSBDb25zaWRlciB0cmFuc2Zvcm1hdGlvbnMgdG8gYWRkcmVzcyBza2V3bmVzcyBhbmQgcmVkdWNlIG91dGxpZXIgZWZmZWN0cy4KICAtIEludmVzdGlnYXRlIGhpZ2gtcHJvZml0IGN1c3RvbWVycyB0byB1bmRlcnN0YW5kIHRoZWlyIGNoYXJhY3RlcmlzdGljcy4KClRoaXMgZGVuc2l0eSBwbG90IGFuYWx5c2lzIGhpZ2hsaWdodHMga2V5IHRyZW5kcyBhbmQgcHJvdmlkZXMgYWN0aW9uYWJsZSByZWNvbW1lbmRhdGlvbnMgZm9yIHByZXByb2Nlc3NpbmcgYW5kIGZ1cnRoZXIgZXhwbG9yYXRpb24uCgotLS0KCiMjIEdlbmRlciBEaXN0cmlidXRpb24gQW5hbHlzaXMKClRoaXMgc2VjdGlvbiB2aXN1YWxpemVzIHRoZSBkaXN0cmlidXRpb24gb2YgZ2VuZGVyIGFjcm9zcyB0aGUgZGF0YXNldCB1c2luZyBhIGJhciBwbG90IGFuZCBhIHBpZSBjaGFydC4KCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9OH0KZ2VuZGVyX2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRmJEdlbmRlcikpCm5hbWVzKGdlbmRlcl9jb3VudHMpIDwtIGMoIkdlbmRlciIsICJDb3VudCIpCgojIENyZWF0ZSBDb3VudCBQbG90IHVzaW5nIGdlb21fY291bnRfZGVmYXVsdApjb3VudF9wbG90IDwtIGdncGxvdChnZW5kZXJfY291bnRzLCBhZXMoeCA9IEdlbmRlciwgeSA9IENvdW50LCBmaWxsID0gR2VuZGVyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgdGhlbWVfcm9zZV9waW5lKCkgKyAgIyBBcHBseSB0aGUgY3VzdG9tIHRoZW1lCiAgc2NhbGVfZmlsbF9yb3NlX3BpbmUoKSArICAjIEFwcGx5IHRoZSBmaWxsIGNvbG9yIHNjYWxlCiAgbGFicyh0aXRsZSA9ICJHZW5kZXIgRGlzdHJpYnV0aW9uIFBsb3QiLCB4ID0gIkdlbmRlciIsIHkgPSAiQ291bnQiKSArICAjIFRpdGxlIGFuZCBheGlzIGxhYmVscwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIndoaXRlIiksICAjIFRpdGxlIGZvbnQgc2l6ZSBhbmQgY29sb3IKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gIndoaXRlIiksICAjIFgtYXhpcyBsYWJlbCBmb250IHNpemUgYW5kIGNvbG9yCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJ3aGl0ZSIpLCAgIyBZLWF4aXMgbGFiZWwgZm9udCBzaXplIGFuZCBjb2xvcgogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAid2hpdGUiKSwgICMgQXhpcyB0aWNrIGxhYmVscyBmb250IHNpemUgYW5kIGNvbG9yCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgY29sb3IgPSAid2hpdGUiKSwgICMgTGVnZW5kIHRpdGxlIGZvbnQgc2l6ZSBhbmQgY29sb3IKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgY29sb3IgPSAid2hpdGUiKSwgICMgTGVnZW5kIHRleHQgZm9udCBzaXplIGFuZCBjb2xvcgogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgxLCAiY20iKSAgIyBBZGp1c3QgdGhlIHNpemUgb2YgbGVnZW5kIGtleXMgKHRoZSBjb2xvciBib3hlcykKICApCgojIEdlbmVyYXRlIHRoZSBQaWUgQ2hhcnQKcGllX2NoYXJ0IDwtIGdlb21fcGllX2RlZmF1bHQoCiAgZGF0YSA9IGdlbmRlcl9jb3VudHMsCiAgY2F0ZWdvcnlfY29sID0gIkdlbmRlciIsCiAgdmFsdWVfY29sID0gIkNvdW50IgopICsgCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCikKCiMgQ29tYmluZSB0aGUgcGxvdHMgdXNpbmcgZ3JpZC5hcnJhbmdlCmdyaWQuYXJyYW5nZShjb3VudF9wbG90LCBwaWVfY2hhcnQsIG5jb2wgPSAyKQpgYGAKIyMjIE9ic2VydmF0aW9ucwoKIyMjIyAqKjEuIEJhciBQbG90KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBUaGUgbWFqb3JpdHkgb2YgY3VzdG9tZXJzIGFyZSAqKk1hbGUqKiwgbWFraW5nIHVwIHRoZSBsYXJnZXN0IGdyb3VwLgogIC0gQSBzbWFsbGVyIHByb3BvcnRpb24gb2YgY3VzdG9tZXJzIGFyZSAqKkZlbWFsZSoqLgogIC0gQSBub3RhYmxlIHBlcmNlbnRhZ2Ugb2YgY3VzdG9tZXJzIGZhbGwgaW50byB0aGUgKipOb3QgRGVmaW5lZCoqIGNhdGVnb3J5LCB3aGljaCBtaWdodCBpbmRpY2F0ZSBtaXNzaW5nIG9yIHVuc3BlY2lmaWVkIGRhdGEuCgojIyMjICoqMi4gUGllIENoYXJ0KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSAqKk1hbGUqKiBjdXN0b21lcnMgY29uc3RpdHV0ZSBhcHByb3hpbWF0ZWx5ICoqNjMuNSUqKiBvZiB0aGUgZGF0YXNldC4KICAtICoqRmVtYWxlKiogY3VzdG9tZXJzIGFjY291bnQgZm9yICoqMjMuNSUqKi4KICAtIFRoZSAqKk5vdCBEZWZpbmVkKiogY2F0ZWdvcnkgcmVwcmVzZW50cyAqKjEzJSoqLgoKIyMjIyAqKlJlY29tbWVuZGF0aW9ucyoqCi0gSW52ZXN0aWdhdGUgdGhlICoqTm90IERlZmluZWQqKiBjYXRlZ29yeSB0byBkZXRlcm1pbmUgd2hldGhlciB0aGlzIGRhdGEgY2FuIGJlIGNsYXJpZmllZCBvciBleGNsdWRlZC4KLSBVc2UgdGhpcyBkaXN0cmlidXRpb24gdG8gc2VnbWVudCBhbmFseXNpcyBvciBtYXJrZXRpbmcgc3RyYXRlZ2llcyBiYXNlZCBvbiBnZW5kZXIuCgotLS0KCiMjIE1hcmtldGluZyBDaGFubmVsIFR5cGUgRGlzdHJpYnV0aW9uCgpUaGlzIHNlY3Rpb24gdmlzdWFsaXplcyB0aGUgZGlzdHJpYnV0aW9uIG9mIG1hcmtldGluZyBjaGFubmVscywgcHJvdmlkaW5nIGluc2lnaHRzIGludG8gd2hpY2ggY2hhbm5lbHMgYXJlIG1vc3QgZWZmZWN0aXZlIGluIGFjcXVpcmluZyBjdXN0b21lcnMuCgpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTh9Cm1hcmtldGluZ19jaGFubmVsX3R5cGVfY291bnQgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShkZiRNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlKSkKbmFtZXMobWFya2V0aW5nX2NoYW5uZWxfdHlwZV9jb3VudCkgPC0gYygiTWFya2V0aW5nX0NoYW5uZWxfVHlwZSIsICJDb3VudCIpCgojIENyZWF0ZSBDb3VudCBQbG90IHVzaW5nIGdlb21fY291bnRfZGVmYXVsdApjb3VudF9wbG90IDwtIGdncGxvdChtYXJrZXRpbmdfY2hhbm5lbF90eXBlX2NvdW50LCBhZXMoeCA9IE1hcmtldGluZ19DaGFubmVsX1R5cGUsIHkgPSBDb3VudCwgZmlsbCA9IE1hcmtldGluZ19DaGFubmVsX1R5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB0aGVtZV9yb3NlX3BpbmUoKSArICAjIEFwcGx5IHRoZSBjdXN0b20gdGhlbWUKICBzY2FsZV9maWxsX3Jvc2VfcGluZSgpICsgICMgQXBwbHkgdGhlIGZpbGwgY29sb3Igc2NhbGUKICBsYWJzKHRpdGxlID0gIk1hcmtldGluZyBDaGFubmVsIFR5cGUgRGlzdHJpYnV0aW9uIiwgeCA9ICIiLCB5ID0gIkNvdW50IikgKyAgIyBUaXRsZSBhbmQgYXhpcyBsYWJlbHMKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJ3aGl0ZSIpLCAgIyBUaXRsZSBmb250IHNpemUgYW5kIGNvbG9yCiAgICAjYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAid2hpdGUiKSwgICMgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZSBhbmQgY29sb3IKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAgIyBIaWRlIHgtYXhpcyB0ZXh0CiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJ3aGl0ZSIpLCAgIyBZLWF4aXMgbGFiZWwgZm9udCBzaXplIGFuZCBjb2xvcgogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAid2hpdGUiLCBhbmdsZSA9IDkwKSwgICMgQXhpcyB0aWNrIGxhYmVscyBmb250IHNpemUgYW5kIGNvbG9yCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgY29sb3IgPSAid2hpdGUiKSwgICMgTGVnZW5kIHRpdGxlIGZvbnQgc2l6ZSBhbmQgY29sb3IKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgY29sb3IgPSAid2hpdGUiKSwgICMgTGVnZW5kIHRleHQgZm9udCBzaXplIGFuZCBjb2xvcgogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgxLCAiY20iKSAgIyBBZGp1c3QgdGhlIHNpemUgb2YgbGVnZW5kIGtleXMgKHRoZSBjb2xvciBib3hlcykKICApCgojIEdlbmVyYXRlIHRoZSBQaWUgQ2hhcnQKcGllX2NoYXJ0IDwtIGdlb21fcGllX2RlZmF1bHQoCiAgZGF0YSA9IG1hcmtldGluZ19jaGFubmVsX3R5cGVfY291bnQsCiAgY2F0ZWdvcnlfY29sID0gIk1hcmtldGluZ19DaGFubmVsX1R5cGUiLAogIHZhbHVlX2NvbCA9ICJDb3VudCIKKSArIAogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAopCgojIENvbWJpbmUgdGhlIHBsb3RzIHVzaW5nIGdyaWQuYXJyYW5nZQpncmlkLmFycmFuZ2UoY291bnRfcGxvdCwgcGllX2NoYXJ0LCBuY29sID0gMikKYGBgCiMjIyBPYnNlcnZhdGlvbnMKCiMjIyMgKioxLiBCYXIgUGxvdCoqCi0gKipPYnNlcnZhdGlvbioqOgogIC0gVGhlICoqRGlyZWN0KiogY2hhbm5lbCBjb250cmlidXRlcyB0aGUgbGFyZ2VzdCBzaGFyZSwgd2l0aCBhcHByb3hpbWF0ZWx5ICoqMzUuOCUqKiBvZiBjdXN0b21lcnMuCiAgLSAqKk9yZ2FuaWMgU2VhcmNoKiogYW5kICoqUGFpZCBTZWFyY2gqKiBhcmUgdGhlIHNlY29uZCBhbmQgdGhpcmQgbW9zdCBjb21tb24gY2hhbm5lbHMsIGF0ICoqMjYuNyUqKiBhbmQgKioyMC44JSoqLCByZXNwZWN0aXZlbHkuCiAgLSAqKkFmZmlsaWF0ZXMqKiByZXByZXNlbnQgKioxMi4zJSoqLCBhbmQgKipQYWlkIFNvY2lhbCoqIGNvbnRyaWJ1dGVzIHRoZSBzbWFsbGVzdCBzaGFyZSBhdCAqKjQuNSUqKi4KCiMjIyMgKioyLiBQaWUgQ2hhcnQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFRoZSBwcm9wb3J0aW9uYWwgdmlldyBjb25maXJtcyB0aGF0ICoqRGlyZWN0KiosICoqT3JnYW5pYyBTZWFyY2gqKiwgYW5kICoqUGFpZCBTZWFyY2gqKiBhcmUgdGhlIGRvbWluYW50IGNoYW5uZWxzLgogIC0gKipQYWlkIFNvY2lhbCoqLCBkZXNwaXRlIGJlaW5nIGEgbWlub3IgY2hhbm5lbCwgbWlnaHQgdGFyZ2V0IG5pY2hlIGN1c3RvbWVyIHNlZ21lbnRzLgoKIyMjIyAqKlJlY29tbWVuZGF0aW9ucyoqCi0gRm9jdXMgbWFya2V0aW5nIGVmZm9ydHMgb24gKipEaXJlY3QqKiBhbmQgKipPcmdhbmljIFNlYXJjaCoqIGNoYW5uZWxzLCBhcyB0aGV5IGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3QuCi0gRXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mICoqUGFpZCBTb2NpYWwqKiB0byBkZXRlcm1pbmUgd2hldGhlciBpdCBpcyBjb3N0LWVmZmVjdGl2ZSBvciByZXF1aXJlcyBhZGp1c3RtZW50cy4KLSBFeHBsb3JlIG9wcG9ydHVuaXRpZXMgdG8gaW1wcm92ZSBjdXN0b21lciBhY3F1aXNpdGlvbiB0aHJvdWdoICoqQWZmaWxpYXRlcyoqIGJ5IHJlZmluaW5nIHRoZSBzdHJhdGVneS4KCi0tLQoKIyMgQ29udGFjdCBBbGxvd2VkIERpc3RyaWJ1dGlvbgoKVGhpcyBzZWN0aW9uIGV4YW1pbmVzIHRoZSBkaXN0cmlidXRpb24gb2YgY3VzdG9tZXIgY29uc2VudCBmb3IgYmVpbmcgY29udGFjdGVkLCByZXByZXNlbnRlZCBieSB0d28gdmlzdWFsaXphdGlvbnM6IGEgYmFyIHBsb3QgYW5kIGEgcGllIGNoYXJ0LgoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD04fQpjb250YWN0X2FsbG93ZWRfY291bnQgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShkZiRDb250YWN0X0FsbG93ZWQpKQpuYW1lcyhjb250YWN0X2FsbG93ZWRfY291bnQpIDwtIGMoIkNvbnRhY3RfQWxsb3dlZCIsICJDb3VudCIpCgojIENyZWF0ZSBDb3VudCBQbG90IHVzaW5nIGdlb21fY291bnRfZGVmYXVsdApjb3VudF9wbG90IDwtIGdncGxvdChjb250YWN0X2FsbG93ZWRfY291bnQsIGFlcyh4ID0gQ29udGFjdF9BbGxvd2VkLCB5ID0gQ291bnQsIGZpbGwgPSBDb250YWN0X0FsbG93ZWQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB0aGVtZV9yb3NlX3BpbmUoKSArICAjIEFwcGx5IHRoZSBjdXN0b20gdGhlbWUKICBzY2FsZV9maWxsX3Jvc2VfcGluZSgpICsgICMgQXBwbHkgdGhlIGZpbGwgY29sb3Igc2NhbGUKICBsYWJzKHRpdGxlID0gIkNvbnRhY3QgQWxsb3dlZCBEaXN0cmlidXRpb24iLCB4ID0gIiIsIHkgPSAiQ291bnQiKSArICAjIFRpdGxlIGFuZCBheGlzIGxhYmVscwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIndoaXRlIiksICAjIFRpdGxlIGZvbnQgc2l6ZSBhbmQgY29sb3IKICAgICNheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJ3aGl0ZSIpLCAgIyBYLWF4aXMgbGFiZWwgZm9udCBzaXplIGFuZCBjb2xvcgogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksICAjIEhpZGUgeC1heGlzIHRleHQKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gIndoaXRlIiksICAjIFktYXhpcyBsYWJlbCBmb250IHNpemUgYW5kIGNvbG9yCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJ3aGl0ZSIsIGFuZ2xlID0gOTApLCAgIyBBeGlzIHRpY2sgbGFiZWxzIGZvbnQgc2l6ZSBhbmQgY29sb3IKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBjb2xvciA9ICJ3aGl0ZSIpLCAgIyBMZWdlbmQgdGl0bGUgZm9udCBzaXplIGFuZCBjb2xvcgogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBjb2xvciA9ICJ3aGl0ZSIpLCAgIyBMZWdlbmQgdGV4dCBmb250IHNpemUgYW5kIGNvbG9yCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDEsICJjbSIpICAjIEFkanVzdCB0aGUgc2l6ZSBvZiBsZWdlbmQga2V5cyAodGhlIGNvbG9yIGJveGVzKQogICkKCiMgR2VuZXJhdGUgdGhlIFBpZSBDaGFydApwaWVfY2hhcnQgPC0gZ2VvbV9waWVfZGVmYXVsdCgKICBkYXRhID0gY29udGFjdF9hbGxvd2VkX2NvdW50LAogIGNhdGVnb3J5X2NvbCA9ICJDb250YWN0X0FsbG93ZWQiLAogIHZhbHVlX2NvbCA9ICJDb3VudCIKKSArIAogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAopCgojIENvbWJpbmUgdGhlIHBsb3RzIHVzaW5nIGdyaWQuYXJyYW5nZQpncmlkLmFycmFuZ2UoY291bnRfcGxvdCwgcGllX2NoYXJ0LCBuY29sID0gMikKYGBgCiMjIyBPYnNlcnZhdGlvbnMKCiMjIyMgKioxLiBCYXIgUGxvdCoqCi0gKipPYnNlcnZhdGlvbioqOgogIC0gQXBwcm94aW1hdGVseSAqKjU3LjclKiogb2YgY3VzdG9tZXJzIGhhdmUgYWxsb3dlZCBjb250YWN0LgogIC0gKio0Mi4zJSoqIG9mIGN1c3RvbWVycyBoYXZlIG9wdGVkIG91dCBvZiBiZWluZyBjb250YWN0ZWQuCgojIyMjICoqMi4gUGllIENoYXJ0KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBUaGUgcGllIGNoYXJ0IGNvbmZpcm1zIHRoYXQgdGhlIG1ham9yaXR5IG9mIGN1c3RvbWVycyBhbGxvdyBjb250YWN0LCB0aG91Z2ggYSBzaWduaWZpY2FudCBwb3J0aW9uIHByZWZlcnMgbm90IHRvIGJlIGNvbnRhY3RlZC4KCiMjIyMgKipSZWNvbW1lbmRhdGlvbnMqKgotIEZvY3VzIG91dHJlYWNoIGFuZCBtYXJrZXRpbmcgc3RyYXRlZ2llcyBvbiB0aGUgKio1Ny43JSoqIG9mIGN1c3RvbWVycyB3aG8gYWxsb3cgY29udGFjdC4KLSBDb25zaWRlciBhbHRlcm5hdGl2ZSwgbm9uLWludHJ1c2l2ZSBtYXJrZXRpbmcgc3RyYXRlZ2llcyBmb3IgdGhlICoqNDIuMyUqKiBvZiBjdXN0b21lcnMgd2hvIGRvIG5vdCBhbGxvdyBjb250YWN0LCBzdWNoIGFzIHBlcnNvbmFsaXplZCBlbWFpbHMgb3IgdGFyZ2V0ZWQgYWRzLgoKLS0tCgojIyBUb3AgMjAgTG9jYXRpb24gQ291bnQgQW5hbHlzaXMKClRoaXMgc2VjdGlvbiBleHBsb3JlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIGN1c3RvbWVyIGNvdW50cyBhY3Jvc3MgdGhlIHRvcCAyMCBsb2NhdGlvbnMgaW4gdGhlIGRhdGFzZXQuIAoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD02fQojIENhbGN1bGF0ZSBHZW5kZXIgQ291bnRzIChMb2NhdGlvbiBDb3VudHMpCmxvY2F0aW9uX2NvdW50cyA8LSBkYXRhLmZyYW1lKHRhYmxlKGRmJExvY2F0aW9uKSkKbmFtZXMobG9jYXRpb25fY291bnRzKSA8LSBjKCJMb2NhdGlvbiIsICJDb3VudCIpCgojIEZpbHRlciBmb3IgdGhlIHRvcCAyMCBsb2NhdGlvbnMgYnkgY291bnQKdG9wXzIwX2xvY2F0aW9ucyA8LSBsb2NhdGlvbl9jb3VudHMgJT4lCiAgYXJyYW5nZShkZXNjKENvdW50KSkgJT4lCiAgaGVhZCgyMCkKCiMgQ291bnQgUGxvdCBmb3IgVG9wIDIwIExvY2F0aW9ucwpjb3VudF9wbG90IDwtIGdncGxvdCh0b3BfMjBfbG9jYXRpb25zLCBhZXMoeCA9IExvY2F0aW9uLCB5ID0gQ291bnQsIGZpbGwgPSBMb2NhdGlvbikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHRoZW1lX3Jvc2VfcGluZSgpICsgICMgQXBwbHkgdGhlIGN1c3RvbSB0aGVtZQogIHNjYWxlX2ZpbGxfcm9zZV9waW5lKCkgKyAgIyBBcHBseSB0aGUgZmlsbCBjb2xvciBzY2FsZQogIGxhYnModGl0bGUgPSAiVG9wIDIwIExvY2F0aW9uIENvdW50IFBsb3QiLCB4ID0gIkxvY2F0aW9uIiwgeSA9ICJDb3VudCIpICsgICMgVGl0bGUgYW5kIGF4aXMgbGFiZWxzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCAKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSAgIyBSb3RhdGUgeC1heGlzIGxhYmVscyBmb3IgYmV0dGVyIHJlYWRhYmlsaXR5CgojIFNob3cgdGhlIGNvdW50IHBsb3QKY291bnRfcGxvdApgYGAKIyMjIE9ic2VydmF0aW9ucwoKIyMjIyAqKkJhciBQbG90KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSAqKkR1YmxpbioqIGRvbWluYXRlcyB0aGUgZGF0YXNldCwgY29udHJpYnV0aW5nIHRoZSBoaWdoZXN0IGNvdW50IG9mIGN1c3RvbWVycywgc2lnbmlmaWNhbnRseSBzdXJwYXNzaW5nIGFsbCBvdGhlciBsb2NhdGlvbnMuCiAgLSBPdGhlciBsb2NhdGlvbnMgc3VjaCBhcyAqKkNvcmsqKiwgKipHYWx3YXkqKiwgYW5kICoqTGltZXJpY2sqKiBjb250cmlidXRlIGEgc21hbGxlciBidXQgbm90aWNlYWJsZSBjb3VudC4KICAtIEEgbG9uZyB0YWlsIGV4aXN0cyBmb3Igb3RoZXIgbG9jYXRpb25zLCBpbmRpY2F0aW5nIGEgcmVsYXRpdmVseSBzbWFsbCByZXByZXNlbnRhdGlvbiBmcm9tIHRoZXNlIGFyZWFzLgoKIyMjIyAqKlJlY29tbWVuZGF0aW9ucyoqCi0gR2l2ZW4gRHVibGluJ3Mgc2lnbmlmaWNhbnQgY29udHJpYnV0aW9uLCBtYXJrZXRpbmcgYW5kIGJ1c2luZXNzIHN0cmF0ZWdpZXMgc2hvdWxkIGZvY3VzIGhlYXZpbHkgb24gdGhpcyByZWdpb24uCi0gRm9yIHNtYWxsZXIgbG9jYXRpb25zLCBjb25zaWRlciByZWdpb25hbCBjYW1wYWlnbnMgb3IgcHJvbW90aW9ucyB0byBpbmNyZWFzZSBjdXN0b21lciBlbmdhZ2VtZW50LgotIEV2YWx1YXRlIGlmIHRoZSBjb25jZW50cmF0aW9uIGluIER1YmxpbiBza2V3cyB0aGUgZGF0YXNldCBvciBsaW1pdHMgaW5zaWdodHMgaW50byBvdGhlciByZWdpb25zLgoKLS0tCgojIyBCb3ggUGxvdCBBbmFseXNpcwoKVGhpcyBzZWN0aW9uIGV4YW1pbmVzIHRoZSBkaXN0cmlidXRpb24gYW5kIHByZXNlbmNlIG9mIG91dGxpZXJzIGFjcm9zcyBzaXgga2V5IHZhcmlhYmxlcyB1c2luZyBib3ggcGxvdHMuIFRoZSB2aXN1YWxpemF0aW9ucyBwcm92aWRlIGEgY2xlYXIgdW5kZXJzdGFuZGluZyBvZiBkYXRhIHZhcmlhYmlsaXR5IGFuZCBwb3RlbnRpYWwgYW5vbWFsaWVzLgoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD04fQojIEdlbmVyYXRlIEJveHBsb3RzIER5bmFtaWNhbGx5CmJveF9wbG90X2xpc3QgPC0gbGFwcGx5KHNlcV9hbG9uZyhkYXRhX2xpc3QpLCBmdW5jdGlvbihpKSB7CiAgZm9ybWF0dGVkX3RpdGxlIDwtIHRvb2xzOjp0b1RpdGxlQ2FzZShnc3ViKCJfIiwgIiAiLCBuYW1lcyhkYXRhX2xpc3QpW2ldKSkKICAKICBnZ3Bsb3QoZGYsIGFlcyh4ID0gZmFjdG9yKDEpLCB5ID0gZGF0YV9saXN0W1tpXV0pKSArCiAgICBnZW9tX2JveHBsb3RfZGVmYXVsdChkYXRhID0gZGYsIHhfY29sID0gImZhY3RvcigxKSIsIHlfY29sID0gbmFtZXMoZGF0YV9saXN0KVtpXSwgZmlsbF9jb2xvciA9IGNvbG9yc1tpXSkgKyAgIyBEeW5hbWljIGNvbG9ycwogICAgbGFicygKICAgICAgdGl0bGUgPSBwYXN0ZSgiIiwgZm9ybWF0dGVkX3RpdGxlKSwKICAgICAgeCA9ICJDYXRlZ29yeSIsIAogICAgICB5ID0gIlZhbHVlIgogICAgKSArCiAgICB0aGVtZV9yb3NlX3BpbmUoKSArICAjIEFwcGx5IFJvc2UgUGluZSB0aGVtZQogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgeC1heGlzIHRleHQKICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgeC1heGlzIHRpY2tzCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSAgIyBUaXRsZSBmb3JtYXR0aW5nCiAgICApCn0pCgojIENvbWJpbmUgQWxsIEJveHBsb3RzIGludG8gYSBHcmlkIExheW91dCB1c2luZyBncmlkLmFycmFuZ2UKZ3JpZC5hcnJhbmdlKGdyb2JzID0gYm94X3Bsb3RfbGlzdCwgbmNvbCA9IGxlbmd0aChzZWxlY3RlZF9jb2xzKSkgICMgQXJyYW5nZSBwbG90cyBob3Jpem9udGFsbHkKYGBgCiMjIyBPYnNlcnZhdGlvbnMKCiMjIyMgKioxLiBGaXJzdCBPcmRlciBQcm9maXQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFRoZSBtYWpvcml0eSBvZiBkYXRhIHBvaW50cyBhcmUgY2x1c3RlcmVkIGJlbG93ICoqMjAgdW5pdHMqKi4KICAtIEEgZmV3IGV4dHJlbWUgb3V0bGllcnMgZXh0ZW5kIHVwIHRvICoqNjAgdW5pdHMqKiwgc3VnZ2VzdGluZyBhIHNrZXdlZCBkaXN0cmlidXRpb24uCgojIyMjICoqMi4gU3Vic2VxdWVudCBPcmRlciBQcm9maXQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIE1vc3QgdmFsdWVzIGFyZSBiZWxvdyAqKjEwMCB1bml0cyoqLCB3aXRoIHNpZ25pZmljYW50IG91dGxpZXJzIHJlYWNoaW5nIGJleW9uZCAqKjQwMCB1bml0cyoqLgogIC0gSGlnaGxpZ2h0cyB2YXJpYWJpbGl0eSBpbiBwcm9maXQgZ2VuZXJhdGlvbiBmcm9tIHN1YnNlcXVlbnQgb3JkZXJzLgoKIyMjIyAqKjMuIFN1YnNlcXVlbnQgT3JkZXJzIENvdW50KioKLSAqKk9ic2VydmF0aW9uKio6CiAgLSBEYXRhIGlzIHRpZ2h0bHkgY2x1c3RlcmVkIGJlbG93ICoqMTAgb3JkZXJzKiosIHdpdGggYSBmZXcgb3V0bGllcnMgZXhjZWVkaW5nICoqMzAgb3JkZXJzKiouCgojIyMjICoqNC4gVG90YWwgVmFsdWUgb2YgQWxsIFByb21vdGlvbnMqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFRoZSBtYWpvcml0eSBvZiBkYXRhIHBvaW50cyBmYWxsIGJlbG93ICoqMjAgdW5pdHMqKi4KICAtIE51bWVyb3VzIGV4dHJlbWUgb3V0bGllcnMgZXhjZWVkICoqMTAwIHVuaXRzKiosIGluZGljYXRpbmcgcHJvbW90aW9uYWwgYW5vbWFsaWVzLgoKIyMjIyAqKjUuIEFnZSoqCi0gKipPYnNlcnZhdGlvbioqOgogIC0gTW9zdCBjdXN0b21lcnMgYXJlIGJldHdlZW4gKioyMCBhbmQgNTAgeWVhcnMgb2xkKiouCiAgLSBGZXcgb3V0bGllcnMgZXh0ZW5kIGJleW9uZCAqKjgwIHllYXJzKiosIHdoaWNoIGNvdWxkIGluZGljYXRlIGRhdGEgaW5jb25zaXN0ZW5jaWVzIG9yIG9sZGVyIGN1c3RvbWVycy4KCiMjIyMgKio2LiBUb3RhbCBQcm9maXQqKgotICoqT2JzZXJ2YXRpb24qKjoKICAtIFRoZSBidWxrIG9mIGRhdGEgaXMgYmVsb3cgKioxMDAgdW5pdHMqKi4KICAtIE91dGxpZXJzIGV4dGVuZCBiZXlvbmQgKio0MDAgdW5pdHMqKiwgcG9pbnRpbmcgdG8gaGlnaC12YWx1ZSBjdXN0b21lcnMgb3IgZGF0YSBpcnJlZ3VsYXJpdGllcy4KCi0tLQoKIyMjIFJlY29tbWVuZGF0aW9ucwotICoqT3V0bGllciBUcmVhdG1lbnQqKjogQ29uc2lkZXIgd2luc29yaXppbmcgb3IgdHJhbnNmb3JtaW5nIGRhdGEgdG8gbWluaW1pemUgdGhlIGluZmx1ZW5jZSBvZiBleHRyZW1lIG91dGxpZXJzLgotICoqRGF0YSBRdWFsaXR5IENoZWNrKio6IFZlcmlmeSB0aGUgYWNjdXJhY3kgb2Ygb3V0bGllciBkYXRhIHBvaW50cywgcGFydGljdWxhcmx5IGZvciB2YXJpYWJsZXMgbGlrZSAqKkFnZSoqIGFuZCAqKlRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKiouCi0gKipGdXJ0aGVyIEFuYWx5c2lzKio6IEludmVzdGlnYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBoaWdoIG91dGxpZXJzIGluICoqVG90YWwgUHJvZml0KiogYW5kIHByb21vdGlvbmFsIGFjdGl2aXR5LgoKLS0tCgojIyBBZ2UgRGlzdHJpYnV0aW9uIGJ5IEdlbmRlcgoKVGhpcyBzZWN0aW9uIGV4cGxvcmVzIHRoZSBkaXN0cmlidXRpb24gb2YgY3VzdG9tZXJzJyBhZ2VzLCBzZWdtZW50ZWQgYnkgZ2VuZGVyLiBUaGUgdmlzdWFsaXphdGlvbiBwcm92aWRlcyBpbnNpZ2h0cyBpbnRvIGFnZSBkZW1vZ3JhcGhpY3MgYW5kIGhpZ2hsaWdodHMgZ2VuZGVyLWJhc2VkIHRyZW5kcy4KCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9Nn0KZ2dwbG90KGRmLCBhZXMoeCA9IEFnZSwgZmlsbCA9IEdlbmRlcikpICsgIyBHcm91cCBieSBHZW5kZXIKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkFnZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyIiwKICAgIHggPSAiQWdlIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICkgKwogIHRoZW1lX3Jvc2VfcGluZSgpICsKICBzY2FsZV9maWxsX3Jvc2VfcGluZSgpCmBgYAojIyMgT2JzZXJ2YXRpb25zCgoxLiAqKk92ZXJhbGwgQWdlIERpc3RyaWJ1dGlvbioqOgogICAtIFRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMgZmFsbCBiZXR3ZWVuICoqMjAgYW5kIDUwIHllYXJzKiouCiAgIC0gQSBzaGFycCBkZWNsaW5lIGluIGZyZXF1ZW5jeSBpcyBvYnNlcnZlZCBiZXlvbmQgKio1MCB5ZWFycyoqLCB3aXRoIHNwYXJzZSBkYXRhIGZvciBjdXN0b21lcnMgYWJvdmUgKio4MCB5ZWFycyoqLgoKMi4gKipHZW5kZXIgU2VnbWVudGF0aW9uKio6CiAgIC0gKipNYWxlcyoqIGZvcm0gdGhlIGxhcmdlc3Qgc2VnbWVudCBhY3Jvc3MgYWxsIGFnZSBncm91cHMuCiAgIC0gKipGZW1hbGVzKiogcmVwcmVzZW50IGEgc21hbGxlciBidXQgY29uc2lzdGVudCBwb3J0aW9uIGFjcm9zcyBtb3N0IGFnZSBncm91cHMuCiAgIC0gKipOb3QgRGVmaW5lZCoqIGdlbmRlciBoYXMgbWluaW1hbCByZXByZXNlbnRhdGlvbiwgcHJpbWFyaWx5IGluIHRoZSB5b3VuZ2VyIGFnZSBicmFja2V0cy4KCjMuICoqUGVha3MgaW4gQWdlKio6CiAgIC0gTm90aWNlYWJsZSBwZWFrcyBhcm91bmQgKioyNeKAkzMwIHllYXJzKiosIGluZGljYXRpbmcgYSBjb25jZW50cmF0ZWQgY3VzdG9tZXIgYmFzZSBpbiB0aGlzIGFnZSByYW5nZS4KCi0tLQoKIyMjIFJlY29tbWVuZGF0aW9ucwotICoqVGFyZ2V0IE1hcmtldGluZyoqOgogIC0gRm9jdXMgbWFya2V0aW5nIGVmZm9ydHMgb24gdGhlICoqMjAtNTAgYWdlIGdyb3VwKiosIGFzIGl0IGNvbnN0aXR1dGVzIHRoZSBtYWpvcml0eSBvZiB0aGUgY3VzdG9tZXIgYmFzZS4KLSAqKkRhdGEgVmFsaWRhdGlvbioqOgogIC0gRW5zdXJlIGFjY3VyYWN5IGZvciBjdXN0b21lcnMgd2l0aCB1bmRlZmluZWQgZ2VuZGVyIG9yIHRob3NlIGFib3ZlICoqODAgeWVhcnMqKiwgYXMgdGhlc2UgbWF5IGluZGljYXRlIGRhdGEgZW50cnkgZXJyb3JzLgoKLS0tCgojIyBJbXBhY3Qgb2YgUHJvbW90aW9ucyBvbiBUb3RhbCBQcm9maXQKClRoaXMgc2VjdGlvbiBpbnZlc3RpZ2F0ZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSAqKlRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKiogYW5kICoqVG90YWwgUHJvZml0KiouIFRoZSBzY2F0dGVyIHBsb3QgcHJvdmlkZXMgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgaG93IHByb21vdGlvbmFsIGFjdGl2aXRpZXMgaW5mbHVlbmNlIGN1c3RvbWVyIHByb2ZpdGFiaWxpdHkuCgpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTZ9CmdncGxvdChkZiwgYWVzKHggPSBUb3RhbF92YWx1ZV9vZl9hbGxfcHJvbW90aW9ucywgeSA9IFRvdGFsX1Byb2ZpdCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9IGNvbG9yc1sxXSkgKyAgIyBDdXN0b20gY29sb3IgZm9yIHBvaW50cyBmcm9tIFJvc8OpIFBpbmUgcGFsZXR0ZQogIGxhYnMoCiAgICB0aXRsZSA9ICJJbXBhY3Qgb2YgUHJvbW90aW9ucyBvbiBUb3RhbCBQcm9maXQiLCAKICAgIHggPSAiVG90YWwgVmFsdWUgb2YgQWxsIFByb21vdGlvbnMiLCAKICAgIHkgPSAiVG90YWwgUHJvZml0IgogICkgKwogIHRoZW1lX3Jvc2VfcGluZSgpICAjIEFwcGx5IHRoZSBjdXN0b20gUm9zw6kgUGluZSB0aGVtZQpgYGAKIyMjIE9ic2VydmF0aW9ucwoKMS4gKipPdmVyYWxsIFJlbGF0aW9uc2hpcCoqOgogICAtIEEgcG9zaXRpdmUgdHJlbmQgaXMgZXZpZGVudCwgaW5kaWNhdGluZyB0aGF0IGhpZ2hlciBwcm9tb3Rpb25hbCB2YWx1ZXMgYXJlIGFzc29jaWF0ZWQgd2l0aCBpbmNyZWFzZWQgdG90YWwgcHJvZml0LgogICAtIEhvd2V2ZXIsIHRoZSBzY2F0dGVycGxvdCBzdWdnZXN0cyAqKmRpbWluaXNoaW5nIHJldHVybnMqKiBhcyBwcm9tb3Rpb25zIGV4Y2VlZCBhIGNlcnRhaW4gdmFsdWUuCgoyLiAqKkNvbmNlbnRyYXRpb24gb2YgRGF0YSoqOgogICAtIEEgbGFyZ2UgY2x1c3RlciBvZiBkYXRhIHBvaW50cyBsaWVzIGluIHRoZSBsb3dlciByYW5nZSBvZiAqKlRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKiosIHN1Z2dlc3RpbmcgdGhhdCBtb3N0IGN1c3RvbWVycyByZWNlaXZlIGxpbWl0ZWQgcHJvbW90aW9ucy4KICAgLSBUaGUgKipUb3RhbCBQcm9maXQqKiBzaG93cyBzaWduaWZpY2FudCB2YXJpYWJpbGl0eSwgZXZlbiBmb3IgY3VzdG9tZXJzIHdpdGggbG93IHByb21vdGlvbmFsIHZhbHVlcy4KCjMuICoqT3V0bGllcnMqKjoKICAgLSBBIGZldyBjdXN0b21lcnMgZXhoaWJpdCBleHRyZW1lbHkgaGlnaCBwcm9tb3Rpb25hbCB2YWx1ZXMgd2l0aCB2YXJ5aW5nIHByb2ZpdCBsZXZlbHMuIFRoZXNlIHBvaW50cyBjb3VsZCByZXByZXNlbnQgc3BlY2lhbCBjYXNlcyBsaWtlIGJ1bGsgZGlzY291bnRzIG9yIGxveWFsdHkgcmV3YXJkcy4KCjQuICoqUG90ZW50aWFsIE5vbi1MaW5lYXIgUGF0dGVybioqOgogICAtIEJleW9uZCBhIGNlcnRhaW4gcHJvbW90aW9uYWwgdGhyZXNob2xkLCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcHJvbW90aW9ucyBhbmQgcHJvZml0IGFwcGVhcnMgbGVzcyBsaW5lYXIsIGhpZ2hsaWdodGluZyB0aGUgbmVlZCBmb3IgZnVydGhlciBpbnZlc3RpZ2F0aW9uLgoKLS0tCgojIyMgUmVjb21tZW5kYXRpb25zCgotICoqT3B0aW1pemluZyBQcm9tb3Rpb25hbCBTdHJhdGVneSoqOgogIC0gSWRlbnRpZnkgdGhlIHRocmVzaG9sZCBmb3IgZGltaW5pc2hpbmcgcmV0dXJucyBhbmQgb3B0aW1pemUgcHJvbW90aW9uYWwgc3BlbmRpbmcgdG8gbWF4aW1pemUgcHJvZml0YWJpbGl0eS4KLSAqKkN1c3RvbWVyIFNlZ21lbnRhdGlvbioqOgogIC0gQW5hbHl6ZSBzZWdtZW50cyBvZiBjdXN0b21lcnMgd2l0aCBoaWdoIHByb21vdGlvbnMgYnV0IGxvdyBwcm9maXRzIHRvIGltcHJvdmUgdGFyZ2V0ZWQgbWFya2V0aW5nIGVmZm9ydHMuCi0gKipPdXRsaWVyIEFuYWx5c2lzKio6CiAgLSBJbnZlc3RpZ2F0ZSBvdXRsaWVycyB3aXRoIGV4dHJlbWVseSBoaWdoIHByb21vdGlvbmFsIHZhbHVlcyB0byB1bmRlcnN0YW5kIHVuaXF1ZSBjYXNlcyBhbmQgcmVmaW5lIG1hcmtldGluZyBzdHJhdGVnaWVzLgoKLS0tCgojIyBUb3RhbCBQcm9maXQgQW5hbHlzaXMKClRoaXMgc2VjdGlvbiBwcm92aWRlcyBpbnNpZ2h0cyBpbnRvIHRvdGFsIHByb2ZpdCBkaXN0cmlidXRpb24gYWNyb3NzIGRpZmZlcmVudCBnZW5kZXJzIGFuZCBtYXJrZXRpbmcgY2hhbm5lbCB0eXBlcy4gVGhlIGJhciBwbG90cyBpbGx1c3RyYXRlIHRoZSB0b3RhbCBwcm9maXQgY29udHJpYnV0aW9uIGZvciBlYWNoIGNhdGVnb3J5LgoKYGBge3IsIGZpZy53aWR0aD0xOSwgZmlnLmhlaWdodD05fQojIFBsb3QgMTogVG90YWwgUHJvZml0IGJ5IEdlbmRlcgpwbG90X2dlbmRlciA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gZmFjdG9yKEdlbmRlciksIHkgPSBUb3RhbF9Qcm9maXQsIGZpbGwgPSBHZW5kZXIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IiwgZnVuID0gInN1bSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyAgIyBTdW1taW5nIHRoZSB0b3RhbCBwcm9maXQgYnkgZ2VuZGVyCiAgbGFicygKICAgIHRpdGxlID0gIlRvdGFsIFByb2ZpdCBieSBHZW5kZXIiLAogICAgeCA9ICJHZW5kZXIiLAogICAgeSA9ICJUb3RhbCBQcm9maXQiCiAgKSArCiAgdGhlbWVfcm9zZV9waW5lKCkgKyAKICBzY2FsZV9maWxsX3Jvc2VfcGluZSgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgICMgTW92ZSBsZWdlbmQgdG8gdGhlIHRvcAoKIyBQbG90IDI6IFRvdGFsIFByb2ZpdCBieSBNYXJrZXRpbmcgQ2hhbm5lbCBUeXBlCnBsb3RfbWFya2V0aW5nX2NoYW5uZWwgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGZhY3RvcihNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlKSwgeSA9IFRvdGFsX1Byb2ZpdCwgZmlsbCA9IE1hcmtldGluZ19DaGFubmVsX1R5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IiwgZnVuID0gInN1bSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyAgIyBTdW1taW5nIHRvdGFsIHByb2ZpdCBieSBtYXJrZXRpbmcgY2hhbm5lbAogIGxhYnMoCiAgICB0aXRsZSA9ICJUb3RhbCBQcm9maXQgYnkgTWFya2V0aW5nIENoYW5uZWwgVHlwZSIsCiAgICB4ID0gIk1hcmtldGluZyBDaGFubmVsIFR5cGUiLAogICAgeSA9ICJUb3RhbCBQcm9maXQiCiAgKSArCiAgdGhlbWVfcm9zZV9waW5lKCkgKwogIHNjYWxlX2ZpbGxfcm9zZV9waW5lKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSAgIyBNb3ZlIGxlZ2VuZCB0byB0aGUgdG9wCgoKIyBNZXJnZSB0aGUgdGhyZWUgcGxvdHMgaW50byBhIGdyaWQgbGF5b3V0CmdyaWQuYXJyYW5nZShwbG90X2dlbmRlciwgcGxvdF9tYXJrZXRpbmdfY2hhbm5lbCwgbmNvbCA9IDIpCgpgYGAKIyMjIE9ic2VydmF0aW9ucwoKIyMjIyBUb3RhbCBQcm9maXQgYnkgR2VuZGVyCjEuICoqTWFsZSBEb21pbmFuY2UqKjoKICAgLSBNYWxlIGN1c3RvbWVycyBjb250cmlidXRlIHRoZSBoaWdoZXN0IHRvdGFsIHByb2ZpdCBjb21wYXJlZCB0byBvdGhlciBnZW5kZXJzLgogICAtIEZlbWFsZSBjdXN0b21lcnMgc2hvdyBhIHNpZ25pZmljYW50IGNvbnRyaWJ1dGlvbiwgYnV0IGl0IGlzIHN0aWxsIG11Y2ggbG93ZXIgdGhhbiBtYWxlIGN1c3RvbWVycy4KICAgLSBUaGUgIk5vdCBEZWZpbmVkIiBnZW5kZXIgY2F0ZWdvcnkgY29udHJpYnV0ZXMgdGhlIGxlYXN0IHRvIHRvdGFsIHByb2ZpdC4KCjIuICoqSW1wbGljYXRpb25zKio6CiAgIC0gTWFya2V0aW5nIHN0cmF0ZWdpZXMgdGFyZ2V0ZWQgdG93YXJkIG1hbGUgY3VzdG9tZXJzIGFwcGVhciB0byBiZSBtb3JlIGVmZmVjdGl2ZSBpbiBkcml2aW5nIHByb2ZpdHMuCiAgIC0gUG90ZW50aWFsIG9wcG9ydHVuaXR5IGV4aXN0cyB0byBpbXByb3ZlIGVuZ2FnZW1lbnQgYW5kIHByb2ZpdCBnZW5lcmF0aW9uIGZyb20gZmVtYWxlIGN1c3RvbWVycy4KCi0tLQoKIyMjIyBUb3RhbCBQcm9maXQgYnkgTWFya2V0aW5nIENoYW5uZWwgVHlwZQoxLiAqKlRvcCBQZXJmb3JtaW5nIENoYW5uZWxzKio6CiAgIC0gKipEaXJlY3QgbWFya2V0aW5nKiogY2hhbm5lbHMgY29udHJpYnV0ZSB0aGUgaGlnaGVzdCB0b3RhbCBwcm9maXQsIHNob3djYXNpbmcgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgZGlyZWN0IGN1c3RvbWVyIGVuZ2FnZW1lbnQuCiAgIC0gKipPcmdhbmljIHNlYXJjaCoqIGFsc28gcGVyZm9ybXMgd2VsbCwgaW5kaWNhdGluZyB0aGUgaW1wb3J0YW5jZSBvZiBTRU8gYW5kIG9yZ2FuaWMgdHJhZmZpYyBpbiBkcml2aW5nIHByb2ZpdGFiaWxpdHkuCgoyLiAqKlVuZGVycGVyZm9ybWluZyBDaGFubmVscyoqOgogICAtICoqUGFpZCBzb2NpYWwqKiBhbmQgKiphZmZpbGlhdGVzKiogY2hhbm5lbHMgY29udHJpYnV0ZSB0aGUgbGVhc3QgdG8gdG90YWwgcHJvZml0LCBzdWdnZXN0aW5nIHRoZSBuZWVkIHRvIGV2YWx1YXRlIGFuZCBvcHRpbWl6ZSBzdHJhdGVnaWVzIGZvciB0aGVzZSBjaGFubmVscy4KCjMuICoqSW1wbGljYXRpb25zKio6CiAgIC0gQnVzaW5lc3NlcyBzaG91bGQgcHJpb3JpdGl6ZSByZXNvdXJjZXMgb24gZGlyZWN0IGFuZCBvcmdhbmljIGNoYW5uZWxzIGZvciBtYXhpbXVtIHByb2ZpdGFiaWxpdHkuCiAgIC0gRXZhbHVhdGUgdGhlIFJPSSBmb3IgdW5kZXJwZXJmb3JtaW5nIGNoYW5uZWxzIGFuZCByZWZpbmUgc3RyYXRlZ2llcyBmb3IgYmV0dGVyIHJldHVybnMuCgotLS0KCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9Nn0KIyBTdW1tYXJpemUgcGVyY2VudGFnZSBkaXN0cmlidXRpb24gb2YgR2VuZGVyIGJ5IE1hcmtldGluZ19DaGFubmVsX1R5cGUKZ2VuZGVyX2NoYW5uZWxfZGlzdHJpYnV0aW9uIDwtIGRmICU+JQogIGdyb3VwX2J5KEdlbmRlciwgTWFya2V0aW5nX0NoYW5uZWxfVHlwZSkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUKICBncm91cF9ieShNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlKSAlPiUKICBtdXRhdGUoUGVyY2VudGFnZSA9IChDb3VudCAvIHN1bShDb3VudCkpICogMTAwKQoKIyBQaXZvdCBkYXRhIGZvciBoZWF0bWFwCmhlYXRtYXBfZGF0YSA8LSBnZW5kZXJfY2hhbm5lbF9kaXN0cmlidXRpb24gJT4lCiAgc2VsZWN0KEdlbmRlciwgTWFya2V0aW5nX0NoYW5uZWxfVHlwZSwgUGVyY2VudGFnZSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IE1hcmtldGluZ19DaGFubmVsX1R5cGUsIHZhbHVlc19mcm9tID0gUGVyY2VudGFnZSkKCiMgTWVsdCB0aGUgZGF0YSBmb3IgZ2dwbG90Cm1lbHRlZF9kYXRhIDwtIG1lbHQoaGVhdG1hcF9kYXRhLCBpZC52YXJzID0gIkdlbmRlciIsIHZhcmlhYmxlLm5hbWUgPSAiTWFya2V0aW5nX0NoYW5uZWxfVHlwZSIsIHZhbHVlLm5hbWUgPSAiUGVyY2VudGFnZSIpCgojIFBsb3QgdGhlIGhlYXRtYXAgd2l0aCBgc2NhbGVfZmlsbF9ncmFkaWVudDJgIGFuZCBSb3PDqSBQaW5lIGNvbG9ycwpnZ3Bsb3QobWVsdGVkX2RhdGEsIGFlcyh4ID0gTWFya2V0aW5nX0NoYW5uZWxfVHlwZSwgeSA9IEdlbmRlciwgZmlsbCA9IFBlcmNlbnRhZ2UpKSArCiAgZ2VvbV90aWxlKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjJmIiwgUGVyY2VudGFnZSkpLCBzaXplID0gNikgKyAjIFNob3cgcGVyY2VudGFnZSB3aXRoIDIgZGVjaW1hbHMKICBzY2FsZV9maWxsX2dyYWRpZW50MigKICAgIGxvdyA9IGNvbG9yc1syXSwgICAgICAjIExvdyBjb2xvcgogICAgbWlkID0gY29sb3JzWzFdLCAgICAgICMgTWlkIGNvbG9yCiAgICBoaWdoID0gY29sb3JzWzNdLCAgICAgIyBIaWdoIGNvbG9yCiAgICBtaWRwb2ludCA9IDAuNSwgICAgICAgICMgQXNzdW1lIG1pZHBvaW50IGF0IDUwJSBmb3IgcGVyY2VudGFnZXMKICAgIGxpbWl0cyA9IGMoMCwgMTAwKSwgICAjIFNldCBsaW1pdHMgZm9yIHBlcmNlbnRhZ2UKICAgIG5hbWUgPSAiUGVyY2VudGFnZSIKICApICsKICB0aGVtZV9yb3NlX3BpbmUoYmFzZV9zaXplID0gMTQpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2VuZGVyIERpc3RyaWJ1dGlvbiBieSBNYXJrZXRpbmcgQ2hhbm5lbCBUeXBlICglKSIsCiAgICB4ID0gIk1hcmtldGluZyBDaGFubmVsIFR5cGUiLAogICAgeSA9ICJHZW5kZXIiCiAgKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTkpLCAjIENlbnRlciBhbmQgYm9sZCB0aXRsZQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTQpLCAjIFJvdGF0ZSB4LWF4aXMgbGFiZWxzCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAjIEFkanVzdCB5LWF4aXMgdGV4dCBzaXplCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAjIFBsYWNlIHRoZSBsZWdlbmQgb24gdGhlIHJpZ2h0CiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAjIEFkanVzdCBsZWdlbmQgdGV4dCBzaXplCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBmYWNlID0gImJvbGQiKSAjIEFkanVzdCBsZWdlbmQgdGl0bGUgc2l6ZQogICkKYGBgCgoKIyMgSW1wYWN0IG9mIFByb21vdGlvbnMgYW5kIE1hcmtldGluZyBDaGFubmVscyBvbiBUb3RhbCBQcm9maXQKClRoaXMgc2VjdGlvbiBleHBsb3JlcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRvdGFsIHZhbHVlIG9mIHByb21vdGlvbnMgYW5kIHRvdGFsIHByb2ZpdCBhY3Jvc3MgdmFyaW91cyBtYXJrZXRpbmcgY2hhbm5lbHMuIFRoZSBzY2F0dGVyIHBsb3QgaGlnaGxpZ2h0cyB0aGUgaW1wYWN0IG9mIG1hcmtldGluZyBlZmZvcnRzIGFuZCBwcm9tb3Rpb25zIG9uIGN1c3RvbWVyIHByb2ZpdGFiaWxpdHkuCgpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTZ9CmdncGxvdChkZiwgYWVzKHggPSBUb3RhbF92YWx1ZV9vZl9hbGxfcHJvbW90aW9ucywgeSA9IFRvdGFsX1Byb2ZpdCwgY29sb3IgPSBNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsgICMgQWRkIHBvaW50cyB3aXRoIHNpemUKICBsYWJzKAogICAgdGl0bGUgPSAiSW1wYWN0IG9mIFByb21vdGlvbnMgYW5kIE1hcmtldGluZyBDaGFubmVscyBvbiBUb3RhbCBQcm9maXQiLCAKICAgIHggPSAiVG90YWwgVmFsdWUgb2YgQWxsIFByb21vdGlvbnMiLCAKICAgIHkgPSAiVG90YWwgUHJvZml0IgogICkgKwogIHRoZW1lX3Jvc2VfcGluZSgpICsgICMgQXBwbHkgdGhlIGN1c3RvbSBSb3PDqSBQaW5lIHRoZW1lCiAgc2NhbGVfY29sb3Jfcm9zZV9waW5lKCkgICMgQXBwbHkgdGhlIFJvc8OpIFBpbmUgY29sb3Igc2NhbGUgdG8gdGhlIGNvbG9yIGFlc3RoZXRpYwpgYGAKIyMjIE9ic2VydmF0aW9ucwoKMS4gKipHZW5lcmFsIFRyZW5kKio6CiAgIC0gQXMgdGhlIHRvdGFsIHZhbHVlIG9mIHByb21vdGlvbnMgaW5jcmVhc2VzLCB0aGVyZSBpcyBhIGNvcnJlc3BvbmRpbmcgaW5jcmVhc2UgaW4gdG90YWwgcHJvZml0LCBidXQgdGhlIHJlbGF0aW9uc2hpcCBhcHBlYXJzIG5vbi1saW5lYXIuCiAgIC0gVGhlIG1ham9yaXR5IG9mIGRhdGEgcG9pbnRzIGNsdXN0ZXIgaW4gdGhlIGxvd2VyIHJhbmdlcyBvZiBwcm9tb3Rpb24gdmFsdWUgYW5kIHByb2ZpdCwgaW5kaWNhdGluZyBtYW55IGN1c3RvbWVycyBnZW5lcmF0ZSBtb2Rlc3QgcHJvZml0cyB3aXRoIGxpbWl0ZWQgcHJvbW90aW9uIGV4cGVuZGl0dXJlLgoKMi4gKipDaGFubmVsLXdpc2UgSW5zaWdodHMqKjoKICAgLSAqKkFmZmlsaWF0ZXMqKiBhbmQgKipQYWlkIFNvY2lhbCoqIHRlbmQgdG8gaGF2ZSBsb3dlciBwcm9maXQgY29udHJpYnV0aW9ucyBldmVuIHdpdGggaW5jcmVhc2VkIHByb21vdGlvbiB2YWx1ZXMuCiAgIC0gKipEaXJlY3QqKiBhbmQgKipPcmdhbmljIFNlYXJjaCoqIGNoYW5uZWxzIGV4aGliaXQgbW9yZSBjb25zaXN0ZW50IHByb2ZpdCBnZW5lcmF0aW9uLCBlc3BlY2lhbGx5IGF0IGhpZ2hlciBwcm9tb3Rpb24gdmFsdWVzLgogICAtICoqUGFpZCBTZWFyY2gqKiBkaXNwbGF5cyBhIHNjYXR0ZXJlZCBwYXR0ZXJuIHdpdGggc29tZSBoaWdoLXByb2ZpdCBvdXRsaWVycy4KCjMuICoqT3V0bGllcnMqKjoKICAgLSBDZXJ0YWluIGN1c3RvbWVycyBhY2hpZXZlIGhpZ2ggcHJvZml0cyB3aXRob3V0IHN1YnN0YW50aWFsIHByb21vdGlvbiB2YWx1ZSwgc3VnZ2VzdGluZyBoaWdoLXZhbHVlIGN1c3RvbWVycyBtYXkgcmVxdWlyZSBsZXNzIGluY2VudGl2ZS4KICAgLSBTb21lIGhpZ2ggcHJvbW90aW9uIHZhbHVlcyBkbyBub3QgY29ycmVzcG9uZCB0byBoaWdoIHByb2ZpdHMsIHNpZ25hbGluZyBpbmVmZmljaWVudCBwcm9tb3Rpb24gc3RyYXRlZ2llcy4KCi0tLQoKIyMjIFJlY29tbWVuZGF0aW9ucwoKLSBGb2N1cyBtYXJrZXRpbmcgZWZmb3J0cyBvbiAqKkRpcmVjdCoqIGFuZCAqKk9yZ2FuaWMgU2VhcmNoKiogY2hhbm5lbHMgdG8gbWF4aW1pemUgcHJvZml0YWJpbGl0eS4KLSBSZWFzc2VzcyBwcm9tb3Rpb24gc3RyYXRlZ2llcyBmb3IgKipBZmZpbGlhdGVzKiogYW5kICoqUGFpZCBTb2NpYWwqKiB0byBpbXByb3ZlIHRoZWlyIHJldHVybiBvbiBpbnZlc3RtZW50IChST0kpLgotIEludmVzdGlnYXRlIGhpZ2gtcHJvZml0IGN1c3RvbWVycyB3aXRoIG1pbmltYWwgcHJvbW90aW9ucyB0byBpZGVudGlmeSBjaGFyYWN0ZXJpc3RpY3MgdGhhdCBjYW4gZ3VpZGUgY3VzdG9tZXIgc2VnbWVudGF0aW9uIGFuZCB0YXJnZXRlZCBjYW1wYWlnbnMuCgotLS0KCiMjIExvY2F0aW9ucyBieSBUb3RhbCBQcm9maXQgYW5kIEdlbmRlcgoKVGhpcyB2aXN1YWxpemF0aW9uIHByZXNlbnRzIHRoZSBkaXN0cmlidXRpb24gb2YgdG90YWwgcHJvZml0IGFjcm9zcyBkaWZmZXJlbnQgbG9jYXRpb25zLCBzZWdtZW50ZWQgYnkgZ2VuZGVyIChGZW1hbGUsIE1hbGUsIGFuZCBOb3QgRGVmaW5lZCkuIFRoZSBhbmFseXNpcyBmb2N1c2VzIG9uIGlkZW50aWZ5aW5nIGdlb2dyYXBoaWNhbCBwcm9maXRhYmlsaXR5IHRyZW5kcy4KCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9CiMgU3VtbWFyaXplIHRoZSB0b3RhbCBwcm9maXQgYnkgTG9jYXRpb24KbG9jYXRpb25fcmV2IDwtIGRmICU+JQogIGdyb3VwX2J5KExvY2F0aW9uKSAlPiUKICBzdW1tYXJpc2UoVG90YWxfUHJvZml0ID0gc3VtKFRvdGFsX1Byb2ZpdCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShUb3RhbF9Qcm9maXQpICU+JQogIG11dGF0ZShMb2NhdGlvbiA9IGZhY3RvcihMb2NhdGlvbiwgbGV2ZWxzID0gLiRMb2NhdGlvbikpCgojIFN1bW1hcml6ZSB0b3RhbCBwcm9maXQgYnkgTG9jYXRpb24gYW5kIEdlbmRlcgpsb2NhdGlvbl9nZW5kZXJfcmV2IDwtIGRmICU+JQogIGdyb3VwX2J5KExvY2F0aW9uLCBHZW5kZXIpICU+JQogIHN1bW1hcmlzZShUb3RhbF9Qcm9maXQgPSBzdW0oVG90YWxfUHJvZml0LCBuYS5ybSA9IFRSVUUpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKExvY2F0aW9uID0gZmFjdG9yKExvY2F0aW9uLCBsZXZlbHMgPSBsb2NhdGlvbl9yZXYkTG9jYXRpb24pKQoKIyBQbG90IHRoZSBkYXRhIGZvciB0aGUgdG9wIDIwIGxvY2F0aW9ucyBieSBUb3RhbF9Qcm9maXQgYW5kIEdlbmRlcgpnZ3Bsb3QobG9jYXRpb25fZ2VuZGVyX3JldiwgYWVzKExvY2F0aW9uLCBUb3RhbF9Qcm9maXQsIGZpbGwgPSBHZW5kZXIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGNvb3JkX2ZsaXAoKSArCiAgZmFjZXRfd3JhcCh+IEdlbmRlcikgKwogIHRoZW1lX3Jvc2VfcGluZSgpICsgICMgQXBwbHkgdGhlIGN1c3RvbSB0aGVtZQogIHNjYWxlX2ZpbGxfcm9zZV9waW5lKCkgKyAgIyBBcHBseSB0aGUgZmlsbCBjb2xvciBzY2FsZQogIGxhYnModGl0bGUgPSAiTG9jYXRpb25zIGJ5IFRvdGFsIFByb2ZpdCBhbmQgR2VuZGVyIiwgeCA9ICJMb2NhdGlvbiIsIHkgPSAiVG90YWwgUHJvZml0IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSAgIyBNb3ZlIHRoZSBsZWdlbmQgdG8gdGhlIHRvcApgYGAKIyMjIE9ic2VydmF0aW9ucwoKMS4gKipPdmVyYWxsIEluc2lnaHRzKio6CiAgIC0gKipEdWJsaW4qKiBzaWduaWZpY2FudGx5IG91dHBlcmZvcm1zIGFsbCBvdGhlciBsb2NhdGlvbnMgaW4gdG90YWwgcHJvZml0IGNvbnRyaWJ1dGlvbnMsIGlycmVzcGVjdGl2ZSBvZiBnZW5kZXIuCiAgIC0gTG9jYXRpb25zIHN1Y2ggYXMgKipDb3JrKiosICoqR2Fsd2F5KiosIGFuZCAqKkxpbWVyaWNrKiogZm9sbG93IER1YmxpbiwgYnV0IHRoZWlyIHByb2ZpdCBjb250cmlidXRpb25zIGFyZSBjb25zaWRlcmFibHkgbG93ZXIuCgoyLiAqKkdlbmRlci1CYXNlZCBEaXN0cmlidXRpb24qKjoKICAgLSAqKk1hbGUgY3VzdG9tZXJzKiogZG9taW5hdGUgdGhlIHRvdGFsIHByb2ZpdCBjb250cmlidXRpb25zIGFjcm9zcyBhbG1vc3QgYWxsIGxvY2F0aW9ucywgaGlnaGxpZ2h0aW5nIHRoZWlyIHNpZ25pZmljYW50IHJvbGUgaW4gcmV2ZW51ZSBnZW5lcmF0aW9uLgogICAtICoqRmVtYWxlIGN1c3RvbWVycyoqIHNob3cgbm90YWJsZSBjb250cmlidXRpb25zIGluIER1YmxpbiBhbmQgYSBmZXcgb3RoZXIgdG9wIGNpdGllcyBidXQgbGFnIGJlaGluZCBtYWxlIGN1c3RvbWVycyBvdmVyYWxsLgogICAtIFRoZSAqKk5vdCBEZWZpbmVkKiogZ2VuZGVyIGdyb3VwIGhhcyBtaW5pbWFsIHByb2ZpdCBjb250cmlidXRpb25zIGFuZCBpcyBtb3N0bHkgY29uY2VudHJhdGVkIGluIGEgZmV3IGxvY2F0aW9ucy4KCjMuICoqR2VvZ3JhcGhpY2FsIERpc3RyaWJ1dGlvbioqOgogICAtIEEgbG9uZyB0YWlsIG9mIHNtYWxsZXIgdG93bnMgY29udHJpYnV0ZXMgbWFyZ2luYWxseSB0byB0b3RhbCBwcm9maXQuIFRoZXNlIGxvY2F0aW9ucyBjb3VsZCBpbmRpY2F0ZSB1bnRhcHBlZCBwb3RlbnRpYWwgb3IgbG93IG1hcmtldCBwZW5ldHJhdGlvbi4KICAgLSBIaWdoIGNvbmNlbnRyYXRpb24gb2YgcHJvZml0IGluIG1ham9yIGNpdGllcyBsaWtlIER1YmxpbiBzdWdnZXN0cyBhIHN0cm9uZyB1cmJhbiBjdXN0b21lciBiYXNlLgoKLS0tCgojIyMgUmVjb21tZW5kYXRpb25zCgotIEZvY3VzIG1hcmtldGluZyBhbmQgY3VzdG9tZXIgZW5nYWdlbWVudCBlZmZvcnRzIG9uIGhpZ2gtcHJvZml0IGNpdGllcyBzdWNoIGFzICoqRHVibGluKiosICoqQ29yayoqLCBhbmQgKipHYWx3YXkqKi4KLSBEZXZlbG9wIHN0cmF0ZWdpZXMgdG8gaW5jcmVhc2UgZmVtYWxlIGN1c3RvbWVyIGVuZ2FnZW1lbnQgaW4gc21hbGxlciB0b3ducyBhbmQgY2l0aWVzLgotIEludmVzdGlnYXRlIHRoZSAiTm90IERlZmluZWQiIGdlbmRlciBncm91cCB0byB1bmRlcnN0YW5kIGl0cyBjaGFyYWN0ZXJpc3RpY3MgYW5kIHBvdGVudGlhbCBmb3IgZ3Jvd3RoLgotIEV4cGxvcmUgb3Bwb3J0dW5pdGllcyBpbiBzbWFsbGVyIHRvd25zIHdpdGggbG93IHByb2ZpdCBjb250cmlidXRpb25zIHRvIGV4cGFuZCBtYXJrZXQgcmVhY2guCgotLS0KCiMjIEltcGFjdCBvZiBQcm9tb3Rpb25zIG9uIFRvdGFsIFByb2ZpdAoKVGhpcyBzY2F0dGVyIHBsb3QgdmlzdWFsaXplcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlICoqVG90YWwgVmFsdWUgb2YgQWxsIFByb21vdGlvbnMqKiBhbmQgdGhlICoqVG90YWwgUHJvZml0KiosIHNob3djYXNpbmcgaG93IHByb21vdGlvbmFsIGVmZm9ydHMgaW5mbHVlbmNlIG92ZXJhbGwgcHJvZml0YWJpbGl0eS4KCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9Nn0KIyBDcmVhdGUgc2NhdHRlciBwbG90IHdpdGggcmVncmVzc2lvbiBsaW5lCmdncGxvdChkZiwgYWVzKHggPSBUb3RhbF92YWx1ZV9vZl9hbGxfcHJvbW90aW9ucywgeSA9IFRvdGFsX1Byb2ZpdCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gY29sb3JzWzFdKSArICAjIFNjYXR0ZXIgcGxvdCB3aXRoIHBvaW50cyBjb2xvcmVkIGluIG9yYW5nZQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gY29sb3JzWzJdLCBzZSA9IEZBTFNFKSArICAjIEFkZCByZWdyZXNzaW9uIGxpbmUKICBsYWJzKAogICAgdGl0bGUgPSAiSW1wYWN0IG9mIFByb21vdGlvbnMgb24gVG90YWwgUHJvZml0IiwKICAgIHggPSAiVG90YWwgVmFsdWUgb2YgQWxsIFByb21vdGlvbnMiLAogICAgeSA9ICJUb3RhbCBQcm9maXQiCiAgKSArCiAgdGhlbWVfcm9zZV9waW5lKCkgICMgQXBwbHkgUm9zZSBQaW5lIHRoZW1lCmBgYAojIyMgT2JzZXJ2YXRpb25zCgoxLiAqKlBvc2l0aXZlIENvcnJlbGF0aW9uKio6CiAgIC0gVGhlIHNjYXR0ZXIgcGxvdCBkZW1vbnN0cmF0ZXMgYSAqKnBvc2l0aXZlIGNvcnJlbGF0aW9uKiogYmV0d2VlbiB0aGUgdG90YWwgdmFsdWUgb2YgcHJvbW90aW9ucyBhbmQgdG90YWwgcHJvZml0LiBBcyB0aGUgcHJvbW90aW9uYWwgdmFsdWUgaW5jcmVhc2VzLCB0aGUgcHJvZml0IGdlbmVyYWxseSByaXNlcy4KICAgLSBUaGUgZml0dGVkIHRyZW5kIGxpbmUgZnVydGhlciBzdXBwb3J0cyB0aGlzIHJlbGF0aW9uc2hpcCwgaW5kaWNhdGluZyB0aGF0IHByb21vdGlvbnMgZWZmZWN0aXZlbHkgY29udHJpYnV0ZSB0byBwcm9maXQgZ2VuZXJhdGlvbi4KCjIuICoqRGltaW5pc2hpbmcgUmV0dXJucyoqOgogICAtIEJleW9uZCBhIGNlcnRhaW4gcHJvbW90aW9uYWwgdmFsdWUgKH41MCB1bml0cyksIHRoZSBpbmNyZW1lbnRhbCBpbmNyZWFzZSBpbiBwcm9maXQgYmVjb21lcyBsZXNzIHByb25vdW5jZWQuCiAgIC0gVGhpcyBzdWdnZXN0cyAqKmRpbWluaXNoaW5nIHJldHVybnMqKiBvbiBpbnZlc3RtZW50IGluIHByb21vdGlvbnMsIHdoZXJlIGZ1cnRoZXIgc3BlbmRpbmcgbWF5IHlpZWxkIG1hcmdpbmFsIHByb2ZpdCBnYWlucy4KCjMuICoqSGlnaCBWYXJpYWJpbGl0eSoqOgogICAtIEEgaGlnaCBjb25jZW50cmF0aW9uIG9mIHBvaW50cyBhdCBsb3dlciBwcm9tb3Rpb25hbCB2YWx1ZXMgKH4wIHRvIDIwIHVuaXRzKSByZWZsZWN0cyB2YXJpYWJpbGl0eSBpbiBwcm9maXRzLCBpbmRpY2F0aW5nIHRoYXQgb3RoZXIgZmFjdG9ycyBtYXkgaW5mbHVlbmNlIGN1c3RvbWVyIGJlaGF2aW9yIGFuZCBwcm9maXRhYmlsaXR5LgoKLS0tCgojIyMgUmVjb21tZW5kYXRpb25zCgotICoqT3B0aW1pemUgUHJvbW90aW9uYWwgU3BlbmRpbmcqKjoKICAtIEZvY3VzIG9uIHByb21vdGlvbmFsIHZhbHVlcyB0aGF0IHlpZWxkIHRoZSBoaWdoZXN0IHJldHVybiBvbiBpbnZlc3RtZW50LCBwb3RlbnRpYWxseSB3aXRoaW4gdGhlIHJhbmdlIG9mIDIwIHRvIDUwIHVuaXRzLgotICoqQW5hbHl6ZSBIaWdoIFZhcmlhYmlsaXR5Kio6CiAgLSBJbnZlc3RpZ2F0ZSBmYWN0b3JzIGNvbnRyaWJ1dGluZyB0byB0aGUgdmFyaWFiaWxpdHkgaW4gcHJvZml0IGZvciBsb3dlciBwcm9tb3Rpb25hbCB2YWx1ZXMgdG8gcmVmaW5lIHN0cmF0ZWdpZXMuCi0gKipUYXJnZXRlZCBQcm9tb3Rpb25zKio6CiAgLSBEZXNpZ24gcHJvbW90aW9ucyB0YXJnZXRlZCBhdCBjdXN0b21lciBzZWdtZW50cyBtb3N0IHJlc3BvbnNpdmUgdG8gaW5jcmVhc2VkIHNwZW5kaW5nLgoKLS0tCgojIDQuIE91dGxpZXIgRGV0ZWN0aW9uIE1ldGhvZHMKClRoaXMgZG9jdW1lbnQgZGVtb25zdHJhdGVzIGhvdyB0byBkZXRlY3Qgb3V0bGllcnMgdXNpbmcgKipaLVNjb3JlKiosICoqTW9kaWZpZWQgWi1TY29yZSoqLCBhbmQgKipJUVIgbWV0aG9kcyoqIG9uIGEgc21hbGwgZGF0YXNldC4gV2Ugd2lsbCBjYWxjdWxhdGUgdGhlIG91dGxpZXJzIHN0ZXAtYnktc3RlcCBmb3IgYSBzaW1wbGUgNS1yb3cgZGF0YXNldC4KCi0tLQoKKipFeGFtcGxlIERhdGFzZXQqKgoKV2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyBkYXRhc2V0OgoKfCBSb3cgfCBWYWx1ZSB8CnwtLS0tLXwtLS0tLS0tfAp8IDEgICB8IDEwICAgIHwKfCAyICAgfCAxMiAgICB8CnwgMyAgIHwgMTQgICAgfAp8IDQgICB8IDEwMCAgIHwKfCA1ICAgfCAxNiAgICB8CgotLS0KCiMjIFotU2NvcmUgTWV0aG9kCgojIyMgRm9ybXVsYQpcWwpaID0gXGZyYWN7KFggLSBcbXUpfXtcc2lnbWF9ClxdCgpXaGVyZToKLSBcKFhcKTogVGhlIHZhbHVlLgotIFwoXG11XCk6IE1lYW4gb2YgdGhlIGRhdGFzZXQuCi0gXChcc2lnbWFcKTogU3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBkYXRhc2V0LgoKIyMjIFN0ZXBzCjEuICoqQ29tcHV0ZSB0aGUgTWVhbiAoXChcbXVcKSkqKjoKICAgXFsKICAgXG11ID0gXGZyYWN7MTAgKyAxMiArIDE0ICsgMTAwICsgMTZ9ezV9ID0gMzAuNAogICBcXQoKMi4gKipDb21wdXRlIHRoZSBTdGFuZGFyZCBEZXZpYXRpb24gKFwoXHNpZ21hXCkpKio6CiAgIFxbCiAgIFxzaWdtYSA9IFxzcXJ0e1xmcmFje1xzdW17KFggLSBcbXUpXjJ9fXtufX0KICAgXF0KICAgU3Vic3RpdHV0aW5nIHZhbHVlczoKICAgXFsKICAgXHNpZ21hID0gXHNxcnR7XGZyYWN7KDEwLTMwLjQpXjIgKyAoMTItMzAuNCleMiArICgxNC0zMC40KV4yICsgKDEwMC0zMC40KV4yICsgKDE2LTMwLjQpXjJ9ezV9fQogICBcXQogICBcWwogICBcc2lnbWEgXGFwcHJveCAzOC44OQogICBcXQoKMy4gKipDb21wdXRlIFotU2NvcmVzKio6CiAgIFVzaW5nIFwoWiA9IFxmcmFjeyhYIC0gXG11KX17XHNpZ21hfVwpOgogICAtIFwoMTA6IFogXGFwcHJveCAtMC41MjRcKQogICAtIFwoMTI6IFogXGFwcHJveCAtMC40NzNcKQogICAtIFwoMTQ6IFogXGFwcHJveCAtMC40MjNcKQogICAtIFwoMTAwOiBaIFxhcHByb3ggMS43OTJcKQogICAtIFwoMTY6IFogXGFwcHJveCAtMC4zNzJcKQoKNC4gKipUaHJlc2hvbGQqKjogT3V0bGllcnMgYXJlIHZhbHVlcyB3aXRoIFwofFp8ID4gM1wpLgoKLS0tCgojIyBNb2RpZmllZCBaLVNjb3JlIE1ldGhvZAoKIyMjIEZvcm11bGEKXFsKTVogPSAwLjY3NDUgXGNkb3QgXGZyYWN7KFggLSBcdGV4dHttZWRpYW59KX17TUFEfQpcXQoKV2hlcmU6Ci0gXChYXCk6IFRoZSB2YWx1ZS4KLSBcKFx0ZXh0e21lZGlhbn1cKTogTWVkaWFuIG9mIHRoZSBkYXRhc2V0LgotIFwoTUFEXCk6IE1lZGlhbiBBYnNvbHV0ZSBEZXZpYXRpb246CiAgXFsKICBNQUQgPSBcdGV4dHtNZWRpYW59KHxYX2kgLSBcdGV4dHttZWRpYW59fCkKICBcXQoKIyMjIFN0ZXBzCjEuICoqQ29tcHV0ZSB0aGUgTWVkaWFuKio6CiAgIFxbCiAgIFx0ZXh0e01lZGlhbn0gPSAxNAogICBcXQoKMi4gKipDb21wdXRlIE1BRCoqOgogICBcWwogICB8MTAtMTR8ID0gNCwgXCwgfDEyLTE0fCA9IDIsIFwsIHwxNC0xNHwgPSAwLCBcLCB8MTAwLTE0fCA9IDg2LCBcLCB8MTYtMTR8ID0gMgogICBcXQogICBNZWRpYW4gb2YgYWJzb2x1dGUgZGV2aWF0aW9uczoKICAgXFsKICAgTUFEID0gXHRleHR7TWVkaWFufShbNCwgMiwgMCwgODYsIDJdKSA9IDIKICAgXF0KCjMuICoqQ29tcHV0ZSBNb2RpZmllZCBaLVNjb3JlcyoqOgogICBVc2luZyBcKE1aID0gMC42NzQ1IFxjZG90IFxmcmFjeyhYIC0gXHRleHR7bWVkaWFufSl9e01BRH1cKToKICAgLSBcKDEwOiBNWiBcYXBwcm94IC0xLjM0OVwpCiAgIC0gXCgxMjogTVogXGFwcHJveCAtMC42NzVcKQogICAtIFwoMTQ6IE1aIFxhcHByb3ggMFwpCiAgIC0gXCgxMDA6IE1aIFxhcHByb3ggMjguOTE2XCkKICAgLSBcKDE2OiBNWiBcYXBwcm94IDAuNjc1XCkKCjQuICoqVGhyZXNob2xkKio6IE91dGxpZXJzIGFyZSB2YWx1ZXMgd2l0aCBcKHxNWnwgPiAzLjVcKS4gSGVuY2UsIFwoMTAwXCkgaXMgYW4gb3V0bGllci4KCi0tLQoKIyMgSVFSIE1ldGhvZAoKIyMjIEZvcm11bGEKT3V0bGllcnMgYXJlIHZhbHVlcyBvdXRzaWRlOgpcWwpbXHRleHR7UTF9IC0gMS41IFxjZG90IFx0ZXh0e0lRUn0sIFx0ZXh0e1EzfSArIDEuNSBcY2RvdCBcdGV4dHtJUVJ9XQpcXQpXaGVyZToKLSBcKFExXCk6IDI1dGggcGVyY2VudGlsZS4KLSBcKFEzXCk6IDc1dGggcGVyY2VudGlsZS4KLSBcKFx0ZXh0e0lRUn0gPSBRMyAtIFExXCkuCgojIyMgU3RlcHMKMS4gKipDb21wdXRlIFF1YXJ0aWxlcyoqOgogICAtIFNvcnRlZCBkYXRhc2V0OiBcKFsxMCwgMTIsIDE0LCAxNiwgMTAwXVwpLgogICAtIFwoUTEgPSAxMiwgUTMgPSAxNlwpLgoKMi4gKipDb21wdXRlIElRUioqOgogICBcWwogICBcdGV4dHtJUVJ9ID0gUTMgLSBRMSA9IDE2IC0gMTIgPSA0CiAgIFxdCgozLiAqKkNvbXB1dGUgT3V0bGllciBCb3VuZHMqKjoKICAgLSBMb3dlciBCb3VuZDoKICAgICBcWwogICAgIFx0ZXh0e0xvd2VyIEJvdW5kfSA9IFExIC0gMS41IFxjZG90IFx0ZXh0e0lRUn0gPSAxMiAtIDEuNSBcY2RvdCA0ID0gNgogICAgIFxdCiAgIC0gVXBwZXIgQm91bmQ6CiAgICAgXFsKICAgICBcdGV4dHtVcHBlciBCb3VuZH0gPSBRMyArIDEuNSBcY2RvdCBcdGV4dHtJUVJ9ID0gMTYgKyAxLjUgXGNkb3QgNCA9IDIyCiAgICAgXF0KCjQuICoqSWRlbnRpZnkgT3V0bGllcnMqKjoKICAgLSBWYWx1ZXMgb3V0c2lkZSBcKFs2LCAyMl1cKSBhcmUgb3V0bGllcnMuIEluIHRoaXMgY2FzZSwgXCgxMDBcKSBpcyBhbiBvdXRsaWVyLgoKLS0tCgojIyBTdW1tYXJ5IFRhYmxlCgp8IFJvdyB8IFZhbHVlIHwgWi1TY29yZSB8IE1vZGlmaWVkIFotU2NvcmUgfCBJUVIgT3V0bGllciB8CnwtLS0tLXwtLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLXwKfCAxICAgfCAxMCAgICB8IC0wLjUyNCAgfCAtMS4zNDkgICAgICAgICAgIHwgTm8gICAgICAgICAgfAp8IDIgICB8IDEyICAgIHwgLTAuNDczICB8IC0wLjY3NSAgICAgICAgICAgfCBObyAgICAgICAgICB8CnwgMyAgIHwgMTQgICAgfCAtMC40MjMgIHwgMC4wMDAgICAgICAgICAgICB8IE5vICAgICAgICAgIHwKfCA0ICAgfCAxMDAgICB8IDEuNzkyICAgfCAyOC45MTYgICAgICAgICAgIHwgWWVzICAgICAgICAgfAp8IDUgICB8IDE2ICAgIHwgLTAuMzcyICB8IDAuNjc1ICAgICAgICAgICAgfCBObyAgICAgICAgICB8CgotLS0KCiMjIEtleSBPYnNlcnZhdGlvbnMKCi0gKipaLVNjb3JlKio6IERldGVjdHMgbm8gb3V0bGllcnMgKFwofFp8ID4gM1wpKS4KLSAqKk1vZGlmaWVkIFotU2NvcmUqKjogRGV0ZWN0cyBcKDEwMFwpIGFzIGFuIG91dGxpZXIgKFwofE1afCA+IDMuNVwpKS4KLSAqKklRUiBNZXRob2QqKjogRGV0ZWN0cyBcKDEwMFwpIGFzIGFuIG91dGxpZXIgKG91dHNpZGUgXChbNiwgMjJdXCkpLgoKLS0tCgojIyBDb25jbHVzaW9uCgotIFVzZSAqKklRUioqIGZvciBzaW1wbGUgRURBIG9yIHJvYnVzdCBjbGVhbmluZy4KLSBVc2UgKipNb2RpZmllZCBaLVNjb3JlKiogZm9yIHNrZXdlZCBkYXRhc2V0cyBvciBzbWFsbCBzYW1wbGUgc2l6ZXMuCi0gVXNlICoqWi1TY29yZSoqIGZvciBzeW1tZXRyaWMsIG5vcm1hbCBkaXN0cmlidXRpb25zLgoKCmBgYHtyfQojIEZpbHRlciBudW1lcmljIGNvbHVtbnMgb25seSBmcm9tIHNlbGVjdGVkX2NvbHMKbnVtZXJpY19kYXRhIDwtIGRmICU+JSBzZWxlY3QoYWxsX29mKHNlbGVjdGVkX2NvbHMpKSAlPiUgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKQoKIyMgMS4gWi1TQ09SRSBNRVRIT0QKel9zY29yZV9vdXRsaWVycyA8LSBudW1lcmljX2RhdGEgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyhldmVyeXRoaW5nKCksIH4gewogICAgel9zY29yZXMgPC0gc2NhbGUoLikgICMgU3RhbmRhcmRpemUgZGF0YQogICAgc3VtKGFicyh6X3Njb3JlcykgPiAzLCBuYS5ybSA9IFRSVUUpICAjIENvdW50IG91dGxpZXJzCiAgfSkpICU+JQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIkNvbHVtbiIsIHZhbHVlc190byA9ICJaX1Njb3JlX091dGxpZXJzIikKCiMjIDIuIE1PRElGSUVEIFotU0NPUkUgTUVUSE9ECm1vZGlmaWVkX3pfb3V0bGllcnMgPC0gbnVtZXJpY19kYXRhICU+JQogIHN1bW1hcmlzZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+IHsKICAgIG1lZGlhbl92YWwgPC0gbWVkaWFuKC4sIG5hLnJtID0gVFJVRSkKICAgIG1hZF92YWwgPC0gbWFkKC4sIGNvbnN0YW50ID0gMS40ODI2LCBuYS5ybSA9IFRSVUUpICAjIE1BRC1iYXNlZCBzY2FsZQogICAgbW9kaWZpZWRfeiA8LSAwLjY3NDUgKiAoLi1tZWRpYW5fdmFsKSAvIG1hZF92YWwKICAgIHN1bShhYnMobW9kaWZpZWRfeikgPiAzLjUsIG5hLnJtID0gVFJVRSkgICMgQ291bnQgcm9idXN0IG91dGxpZXJzCiAgfSkpICU+JQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIkNvbHVtbiIsIHZhbHVlc190byA9ICJNb2RpZmllZF9aX091dGxpZXJzIikKCiMjIDMuIElRUiBNRVRIT0QKaXFyX291dGxpZXJzIDwtIG51bWVyaWNfZGF0YSAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfiB7CiAgICBRMSA8LSBxdWFudGlsZSguLCAwLjI1LCBuYS5ybSA9IFRSVUUpCiAgICBRMyA8LSBxdWFudGlsZSguLCAwLjc1LCBuYS5ybSA9IFRSVUUpCiAgICBJUVIgPC0gUTMgLSBRMQogICAgbG93ZXJfYm91bmQgPC0gUTEgLSAxLjUgKiBJUVIKICAgIHVwcGVyX2JvdW5kIDwtIFEzICsgMS41ICogSVFSCiAgICBzdW0oLiA8IGxvd2VyX2JvdW5kIHwgLiA+IHVwcGVyX2JvdW5kLCBuYS5ybSA9IFRSVUUpICAjIENvdW50IElRUiBvdXRsaWVycwogIH0pKSAlPiUKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJDb2x1bW4iLCB2YWx1ZXNfdG8gPSAiSVFSX091dGxpZXJzIikKCiMjIENvbWJpbmUgUmVzdWx0cyBmb3IgQWxsIE1ldGhvZHMKb3V0bGllcl9zdW1tYXJ5IDwtIHpfc2NvcmVfb3V0bGllcnMgJT4lCiAgZnVsbF9qb2luKG1vZGlmaWVkX3pfb3V0bGllcnMsIGJ5ID0gIkNvbHVtbiIpICU+JQogIGZ1bGxfam9pbihpcXJfb3V0bGllcnMsIGJ5ID0gIkNvbHVtbiIpCgojIFByaW50IHRoZSBmdWxsIHN1bW1hcnkKcHJpbnQob3V0bGllcl9zdW1tYXJ5KQpgYGAKLS0tCgojIDUuIEhhbmRsaW5nIE91dGxpZXJzCgpXaGVuIGRlYWxpbmcgd2l0aCBvdXRsaWVyIHJlbW92YWwsIHR3byBwb3B1bGFyIElRUi1iYXNlZCBtZXRob2RzIGFyZToKCjEuICoqQ29sdW1uLVdpc2UgRmlsdGVyaW5nKio6IFJlbW92ZXMgb3V0bGllcnMgZm9yIGVhY2ggY29sdW1uIGluZGl2aWR1YWxseS4KMi4gKipSb3ctV2lzZSBGaWx0ZXJpbmcqKjogUmVtb3ZlcyBlbnRpcmUgcm93cyBpZiBhbnkgY29sdW1uIGNvbnRhaW5zIGFuIG91dGxpZXIuCgpUaGlzIGRvY3VtZW50IGV4cGxvcmVzIHRoZWlyIGNoYXJhY3RlcmlzdGljcywgYWR2YW50YWdlcywgZGlzYWR2YW50YWdlcywgYW5kIHVzZSBjYXNlcy4KCi0tLQoKIyMgQ29sdW1uLVdpc2UgRmlsdGVyaW5nCgpDb2x1bW4tV2lzZSBGaWx0ZXJpbmcgaXMgYSBsZXNzIGFnZ3Jlc3NpdmUgYXBwcm9hY2ggdG8gb3V0bGllciByZW1vdmFsLiBJdCBkZXRlY3RzIGFuZCByZW1vdmVzIG91dGxpZXJzIGZyb20gZWFjaCBjb2x1bW4gKippbmRpdmlkdWFsbHkqKiB3aGlsZSByZXRhaW5pbmcgcm93cyB0aGF0IGFyZSB2YWxpZCBmb3Igb3RoZXIgY29sdW1ucy4gVGhpcyBtZXRob2QgaXMgZXNwZWNpYWxseSB1c2VmdWwgZm9yIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgKEVEQSksIHdoZXJlIHJldGFpbmluZyBhcyBtdWNoIGRhdGEgYXMgcG9zc2libGUgaXMgZXNzZW50aWFsLgoKYGBge3J9CnJlbW92ZV9vdXRsaWVyc19jb2x1bW53aXNlIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHMpIHsKICBmb3IgKGNvbCBpbiBjb2xzKSB7CiAgICBRMSA8LSBxdWFudGlsZShkYXRhW1tjb2xdXSwgMC4yNSwgbmEucm0gPSBUUlVFKQogICAgUTMgPC0gcXVhbnRpbGUoZGF0YVtbY29sXV0sIDAuNzUsIG5hLnJtID0gVFJVRSkKICAgIElRUiA8LSBRMyAtIFExCiAgICBsb3dlcl9ib3VuZCA8LSBRMSAtIDEuNSAqIElRUgogICAgdXBwZXJfYm91bmQgPC0gUTMgKyAxLjUgKiBJUVIKICAgIGRhdGEgPC0gZGF0YSAlPiUgZmlsdGVyKGRhdGFbW2NvbF1dID49IGxvd2VyX2JvdW5kICYgZGF0YVtbY29sXV0gPD0gdXBwZXJfYm91bmQpCiAgfQogIHJldHVybihkYXRhKQp9CmBgYAoKKipFeGFtcGxlIEV4ZWN1dGlvbioqCmBgYHtyfQpjbGVhbmVkX2RmX2NvbHVtbndpc2UgPC0gcmVtb3ZlX291dGxpZXJzX2NvbHVtbndpc2UoZGYsIHNlbGVjdGVkX2NvbHMpCmNhdCgiT3JpZ2luYWwgUm93czoiLCBucm93KGRmKSkKY2F0KCJDbGVhbmVkIFJvd3M6IiwgbnJvdyhjbGVhbmVkX2RmX2NvbHVtbndpc2UpKQpgYGAKCiMjIyBDaGFyYWN0ZXJpc3RpY3Mgb2YgQ29sdW1uLVdpc2UgRmlsdGVyaW5nCgojIyMjIEhvdyBJdCBXb3JrcwoKLSBPdXRsaWVycyBhcmUgZGV0ZWN0ZWQgYW5kIHJlbW92ZWQgZm9yIGVhY2ggY29sdW1uICoqaW5kZXBlbmRlbnRseSoqLgotIFJvd3MgYXJlIHJldGFpbmVkIHVubGVzcyBmbGFnZ2VkIGFzIGFuIG91dGxpZXIgaW4gdGhlICoqY3VycmVudCBjb2x1bW4qKiBiZWluZyBwcm9jZXNzZWQuCgojIyMjIEFkdmFudGFnZXMKCi0gUmV0YWlucyBtb3JlIGRhdGEgb3ZlcmFsbCwgYXMgcm93cyB2YWxpZCBpbiBvdGhlciBjb2x1bW5zIGFyZSAqKm5vdCByZW1vdmVkKiouCi0gTGVzcyBhZ2dyZXNzaXZlLCBtYWtpbmcgaXQgc3VpdGFibGUgZm9yICoqZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyAoRURBKSoqIG9yIGluaXRpYWwgY2xlYW5pbmcgc3RlcHMuCgojIyMjIERpc2FkdmFudGFnZXMKCi0gSW5jb25zaXN0ZW50IGNsZWFuaW5nOiBBIHJvdyBmbGFnZ2VkIGFzIGFuIG91dGxpZXIgaW4gb25lIGNvbHVtbiBidXQgdmFsaWQgaW4gYW5vdGhlciByZW1haW5zLCBwb3RlbnRpYWxseSBsZWFkaW5nIHRvICoqaW5jb25zaXN0ZW50IHJlc3VsdHMqKi4KCi0tLQoKIyMgUm93LVdpc2UgRmlsdGVyaW5nCgpgYGB7cn0KcmVtb3ZlX291dGxpZXJzX2lxciA8LSBmdW5jdGlvbihkYXRhLCBjb2xzKSB7CiAgZGF0YSAlPiUKICAgIGZpbHRlcihhY3Jvc3MoYWxsX29mKGNvbHMpLCB+IHsKICAgICAgUTEgPC0gcXVhbnRpbGUoLiwgMC4yNSwgbmEucm0gPSBUUlVFKQogICAgICBRMyA8LSBxdWFudGlsZSguLCAwLjc1LCBuYS5ybSA9IFRSVUUpCiAgICAgIElRUiA8LSBRMyAtIFExCiAgICAgIGxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSCiAgICAgIHVwcGVyX2JvdW5kIDwtIFEzICsgMS41ICogSVFSCiAgICAgIC4gPj0gbG93ZXJfYm91bmQgJiAuIDw9IHVwcGVyX2JvdW5kCiAgICB9KSkKfQpgYGAKCioqRXhhbXBsZSBFeGVjdXRpb24qKgpgYGB7cn0KIyBFeGFtcGxlOiBBcHBseWluZyBSb3ctV2lzZSBGaWx0ZXJpbmcKY2xlYW5lZF9kZl9yb3d3aXNlIDwtIHJlbW92ZV9vdXRsaWVyc19pcXIoZGYsIHNlbGVjdGVkX2NvbHMpCgojIE91dHB1dDogQmVmb3JlIGFuZCBBZnRlciBSb3cgQ291bnRzCmNhdCgiT3JpZ2luYWwgUm93czoiLCBucm93KGRmKSkKY2F0KCJDbGVhbmVkIFJvd3M6IiwgbnJvdyhjbGVhbmVkX2RmX3Jvd3dpc2UpKQpgYGAKCiMjIyBDaGFyYWN0ZXJpc3RpY3Mgb2YgUm93LVdpc2UgRmlsdGVyaW5nCgojIyMjIEhvdyBJdCBXb3JrcwoKLSBPdXRsaWVycyBhcmUgZGV0ZWN0ZWQgYWNyb3NzIGFsbCBzZWxlY3RlZCBjb2x1bW5zICoqYXQgdGhlIHNhbWUgdGltZSoqLgotIElmICoqYW55IGNvbHVtbioqIGluIGEgcm93IGNvbnRhaW5zIGFuIG91dGxpZXIsIHRoZSAqKmVudGlyZSByb3cgaXMgcmVtb3ZlZCoqLgoKIyMjIyBBZHZhbnRhZ2VzCgotIEVuc3VyZXMgKipjb25zaXN0ZW50IGNsZWFuaW5nKiogYWNyb3NzIGFsbCBzZWxlY3RlZCBjb2x1bW5zLgotIFN1aXRhYmxlIGZvciAqKm1hY2hpbmUgbGVhcm5pbmcgcGlwZWxpbmVzKiogb3IgKipzdHJpY3Qgc3RhdGlzdGljYWwgYW5hbHlzaXMqKiwgd2hlcmUgY2xlYW4gYW5kIGNvbXBsZXRlIGRhdGEgaXMgY3J1Y2lhbC4KCiMjIyMgRGlzYWR2YW50YWdlcwoKLSBNb3JlIGFnZ3Jlc3NpdmU6IFRoaXMgYXBwcm9hY2ggb2Z0ZW4gbGVhZHMgdG8gKipzaWduaWZpY2FudCBkYXRhIGxvc3MqKiB3aGVuIHZhcmlhYmlsaXR5IGV4aXN0cyBhY3Jvc3MgbXVsdGlwbGUgY29sdW1ucy4KCi0tLQoKIyMgV2hpY2ggT25lIFNob3VsZCBZb3UgVXNlPwoKU2VsZWN0aW5nIHRoZSBhcHByb3ByaWF0ZSBvdXRsaWVyIHJlbW92YWwgbWV0aG9kIGRlcGVuZHMgb24geW91ciBnb2FsLiBIZXJl4oCZcyBhIHN0cmFpZ2h0Zm9yd2FyZCBndWlkZSB0byBoZWxwIHlvdSBkZWNpZGUuCgotLS0KCioqSWYgWW91ciBHb2FsIGlzIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgb3IgWW91IFdhbnQgdG8gUmV0YWluIGFzIE11Y2ggRGF0YSBhcyBQb3NzaWJsZToqKgoKLSAqKlVzZSoqOiBgcmVtb3ZlX291dGxpZXJzX2NvbHVtbndpc2VgCi0gKipXaHkgQ2hvb3NlIFRoaXMgTWV0aG9kPyoqCiAgLSBUaGlzIGFwcHJvYWNoIGlzICoqbGVzcyBhZ2dyZXNzaXZlKiosIHJlbW92aW5nIG91dGxpZXJzIGNvbHVtbiBieSBjb2x1bW4uCiAgLSBSb3dzIHRoYXQgYXJlIHZhbGlkIGluIG90aGVyIGNvbHVtbnMgcmVtYWluIGludGFjdCwgYWxsb3dpbmcgeW91IHRvIHJldGFpbiBtb3JlIG9mIHlvdXIgZGF0YXNldC4KICAtIFBlcmZlY3QgZm9yICoqaW5pdGlhbCBkYXRhIGNsZWFuaW5nKiogb3IgKiplYXJseS1zdGFnZSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIChFREEpKiosIHdoZXJlIHByZXNlcnZpbmcgZGF0YSBmb3IgYSBicm9hZGVyIG92ZXJ2aWV3IGlzIGVzc2VudGlhbC4KCi0tLQoKKipJZiBZb3VyIEdvYWwgaXMgTWFjaGluZSBMZWFybmluZyBvciBTdGF0aXN0aWNhbCBNb2RlbGluZywgV2hlcmUgQ29uc2lzdGVuY3kgQWNyb3NzIFJvd3MgaXMgQ3JpdGljYWw6KioKCi0gKipVc2UqKjogYHJlbW92ZV9vdXRsaWVyc19pcXJgCi0gKipXaHkgQ2hvb3NlIFRoaXMgTWV0aG9kPyoqCiAgLSBUaGlzIG1ldGhvZCBpcyAqKm1vcmUgc3RyaW5nZW50KiosIHJlbW92aW5nIGVudGlyZSByb3dzIGlmIGFueSBjb2x1bW4gY29udGFpbnMgYW4gb3V0bGllci4KICAtIEVuc3VyZXMgdGhhdCB5b3VyIGRhdGFzZXQgaXMgKipjb25zaXN0ZW50KiogYW5kIGZyZWUgZnJvbSBvdXRsaWVycyBpbiB0aGUgc3BlY2lmaWVkIGNvbHVtbnMuCiAgLSBJZGVhbCBmb3IgKiptYWNoaW5lIGxlYXJuaW5nIHBpcGVsaW5lcyoqIG9yICoqc3RhdGlzdGljYWwgbW9kZWxpbmcqKiwgd2hlcmUgY2xlYW4gYW5kIGNvbnNpc3RlbnQgZGF0YSBpcyBjcnVjaWFsIGZvciBhY2N1cmF0ZSByZXN1bHRzLgoKLS0tCgojIyBRdWljayBDb21wYXJpc29uCgp8IEdvYWwgICAgICAgICAgICAgICAgICAgICB8IFJlY29tbWVuZGVkIE1ldGhvZCAgICAgICAgIHwgV2h5PyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8ICoqRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKSoqIHwgYHJlbW92ZV9vdXRsaWVyc19jb2x1bW53aXNlYCB8IFJldGFpbnMgbW9yZSBkYXRhIGZvciBleHBsb3JhdGlvbi4gICAgfAp8ICoqTWFjaGluZSBMZWFybmluZy9Nb2RlbGluZyoqICAgICAgIHwgYHJlbW92ZV9vdXRsaWVyc19pcXJgICAgICAgIHwgRW5zdXJlcyBzdHJpY3QgY29uc2lzdGVuY3kgYWNyb3NzIHJvd3MufAoKLS0tCgojIyMgRmluYWwgVGhvdWdodHMKCi0gVXNlICoqQ29sdW1uLVdpc2UgRmlsdGVyaW5nKiogKGByZW1vdmVfb3V0bGllcnNfY29sdW1ud2lzZWApIHdoZW4gZXhwbG9yaW5nIGRhdGEgYW5kIGFpbWluZyB0byBwcmVzZXJ2ZSBhcyBtdWNoIGluZm9ybWF0aW9uIGFzIHBvc3NpYmxlLgotIFVzZSAqKlJvdy1XaXNlIEZpbHRlcmluZyoqIChgcmVtb3ZlX291dGxpZXJzX2lxcmApIHdoZW4gY29uc2lzdGVuY3kgYWNyb3NzIHJvd3MgaXMgY3JpdGljYWwgZm9yIGRvd25zdHJlYW0gdGFza3MgbGlrZSBtb2RlbGluZyBvciBhbmFseXNpcy4KCk5vdGU6IEFsd2F5cyBldmFsdWF0ZSB0aGUgaW1wYWN0IG9mIHlvdXIgY2hvc2VuIG1ldGhvZCBvbiB0aGUgZGF0YXNldCB0byBlbnN1cmUgaXQgYWxpZ25zIHdpdGggeW91ciBnb2Fscy4KCi0tLQoKIyMgVmlvbGluIFBsb3RzIENvbHVtbi1XaXNlIEZpbHRlcmluZwoKVmlvbGluIHBsb3RzIHByb3ZpZGUgYSBkZXRhaWxlZCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIGRhdGEsIGNvbWJpbmluZyB0aGUgaW5mb3JtYXRpb24gb2YgYSBib3ggcGxvdCBhbmQgYSBkZW5zaXR5IHBsb3QuIFRoaXMgdmlzdWFsaXphdGlvbiBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBpZGVudGlmeWluZyBwYXR0ZXJucywgc3ByZWFkLCBhbmQgcG90ZW50aWFsIG91dGxpZXJzIHdpdGhpbiBhIGRhdGFzZXQuCgpUaGUgZm9sbG93aW5nIHZpb2xpbiBwbG90cyBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb25zIG9mIGtleSBmZWF0dXJlcyBpbiB0aGUgZGF0YXNldCAqKmFmdGVyIGFwcGx5aW5nIGNvbHVtbi13aXNlIG91dGxpZXIgcmVtb3ZhbCB1c2luZyB0aGUgSVFSIG1ldGhvZCoqLiBUaGUgc2VsZWN0ZWQgZmVhdHVyZXMgaW5jbHVkZToKCjEuICoqRmlyc3QgT3JkZXIgUHJvZml0KioKMi4gKipTdWJzZXF1ZW50IE9yZGVyIFByb2ZpdCoqCjMuICoqU3Vic2VxdWVudCBPcmRlcnMgQ291bnQqKgo0LiAqKlRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zKioKNS4gKipBZ2UqKgo2LiAqKlRvdGFsIFByb2ZpdCoqCgotLS0KCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9OH0KIyBTdGVwIDM6IEdlbmVyYXRlIFZpb2xpbiBQbG90cyBEeW5hbWljYWxseQp2aW9saW5fcGxvdF9saXN0IDwtIGxhcHBseShzZXFfYWxvbmcoc2VsZWN0ZWRfY29scyksIGZ1bmN0aW9uKGkpIHsKICBjb2xfbmFtZSA8LSBzZWxlY3RlZF9jb2xzW2ldCiAgZm9ybWF0dGVkX3RpdGxlIDwtIHRvb2xzOjp0b1RpdGxlQ2FzZShnc3ViKCJfIiwgIiAiLCBjb2xfbmFtZSkpCiAgCiAgZ2dwbG90KGNsZWFuZWRfZGZfY29sdW1ud2lzZSwgYWVzKHggPSBmYWN0b3IoMSksIHkgPSAuZGF0YVtbY29sX25hbWVdXSkpICsKICAgIGdlb21fdmlvbGluKGZpbGwgPSBjb2xvcnNbaV0sIGNvbG9yID0gY29sb3JzW2ldLCBhbW91bnQgPSAwLjIpICsgICMgRHluYW1pYyBjb2xvcnMKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gcGFzdGUoIiIsIGZvcm1hdHRlZF90aXRsZSksCiAgICAgIHggPSAiIiwgCiAgICAgIHkgPSAiVmFsdWUiCiAgICApICsKICAgIHRoZW1lX3Jvc2VfcGluZSgpICsgICMgQXBwbHkgUm9zZSBQaW5lIHRoZW1lCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksICAjIFJlbW92ZSB4LWF4aXMgdGV4dAogICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksICAjIFJlbW92ZSB4LWF4aXMgdGlja3MKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpICAjIFRpdGxlIGZvcm1hdHRpbmcKICAgICkKfSkKCiMgU3RlcCA0OiBDb21iaW5lIEFsbCBWaW9saW4gUGxvdHMgaW50byBhIEdyaWQgTGF5b3V0CmdyaWQuYXJyYW5nZShncm9icyA9IHZpb2xpbl9wbG90X2xpc3QsIG5jb2wgPSBsZW5ndGgoc2VsZWN0ZWRfY29scykpCmBgYAojIyMgT2JzZXJ2YXRpb25zCgojIyMjIEZpcnN0IE9yZGVyIFByb2ZpdAotIFRoZSBtYWpvcml0eSBvZiB0aGUgdmFsdWVzIGFyZSBjb25jZW50cmF0ZWQgd2l0aGluIHRoZSBsb3dlciByYW5nZSwgaW5kaWNhdGluZyBtb3N0IGN1c3RvbWVycycgZmlyc3Qgb3JkZXJzIHlpZWxkIHJlbGF0aXZlbHkgbG93IHByb2ZpdC4KLSBBIHNtYWxsIHRhaWwgc3VnZ2VzdHMgYSBmZXcgY3VzdG9tZXJzIGhhdmUgc2lnbmlmaWNhbnRseSBoaWdoZXIgcHJvZml0cy4KCiMjIyMgU3Vic2VxdWVudCBPcmRlciBQcm9maXQKLSBTaW1pbGFyIHRvIHRoZSBmaXJzdCBvcmRlciwgdGhlIGRlbnNpdHkgaXMgY29uY2VudHJhdGVkIGF0IHRoZSBsb3dlciBlbmQgd2l0aCBmZXdlciBjdXN0b21lcnMgY29udHJpYnV0aW5nIHRvIGhpZ2hlciBwcm9maXRzLgoKIyMjIyBTdWJzZXF1ZW50IE9yZGVycyBDb3VudAotIE1vc3QgY3VzdG9tZXJzIGhhdmUgcGxhY2VkIGEgbGltaXRlZCBudW1iZXIgb2Ygc3Vic2VxdWVudCBvcmRlcnMuCi0gVGhlIGRpc3RyaWJ1dGlvbiByZXZlYWxzIGEgc21hbGwgbnVtYmVyIG9mIGN1c3RvbWVycyB3aXRoIG1hbnkgc3Vic2VxdWVudCBvcmRlcnMuCgojIyMjIFRvdGFsIFZhbHVlIG9mIEFsbCBQcm9tb3Rpb25zCi0gUHJvbW90aW9ucyBhcmUgY29uY2VudHJhdGVkIGF0IGxvdyB2YWx1ZXMsIHN1Z2dlc3RpbmcgbGltaXRlZCB1c2FnZSBvciBpbXBhY3QgZm9yIHRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMuCi0gVGhlIGRlbnNpdHkgcGxvdCBzaG93cyBhIHNoYXJwIGRlY2xpbmUgYXMgdGhlIHZhbHVlIGluY3JlYXNlcy4KCiMjIyMgQWdlCi0gQWdlIGRpc3RyaWJ1dGlvbiBpcyBmYWlybHkgdW5pZm9ybSBpbiB0aGUgbWlkZGxlIHJhbmdlICgyMOKAkzQwIHllYXJzKSwgdGFwZXJpbmcgb2ZmIGZvciB5b3VuZ2VyIGFuZCBvbGRlciBjdXN0b21lcnMuCgojIyMjIFRvdGFsIFByb2ZpdAotIFRoZSBvdmVyYWxsIHByb2ZpdCBpcyBwcmltYXJpbHkgbG93LCB3aXRoIGEgZmV3IGN1c3RvbWVycyBjb250cmlidXRpbmcgdG8gaGlnaCBwcm9maXRzLgoKLS0tCgojIyMgQWR2YW50YWdlcyBvZiBWaW9saW4gUGxvdHMKLSBDb21iaW5lcyB0aGUgZmVhdHVyZXMgb2YgYSBib3ggcGxvdCBhbmQgZGVuc2l0eSBwbG90LCBtYWtpbmcgaXQgZWFzaWVyIHRvIGlkZW50aWZ5IHRoZSBzcHJlYWQgYW5kIGRlbnNpdHkgb2YgZGF0YS4KLSBIaWdobGlnaHRzIHBvdGVudGlhbCBvdXRsaWVycyB2aXN1YWxseSB3aGlsZSByZXRhaW5pbmcgdGhlIG92ZXJhbGwgc2hhcGUgb2YgdGhlIGRpc3RyaWJ1dGlvbi4KCiMjIyBMaW1pdGF0aW9ucwotIERvZXMgbm90IHByb3ZpZGUgYSBkaXJlY3QgY291bnQgb2Ygb3V0bGllcnMuCi0gSW50ZXJwcmV0YXRpb24gY2FuIGJlIGNoYWxsZW5naW5nIGZvciBmZWF0dXJlcyB3aXRoIGhpZ2hseSBza2V3ZWQgZGlzdHJpYnV0aW9ucy4KCi0tLQoKIyMjIENvbmNsdXNpb24KClRoZSB2aW9saW4gcGxvdHMgb2ZmZXIgYSBjb21wcmVoZW5zaXZlIG92ZXJ2aWV3IG9mIHRoZSBjbGVhbmVkIGRhdGEgZGlzdHJpYnV0aW9ucywgaGlnaGxpZ2h0aW5nIHRoZSBjb25jZW50cmF0aW9uIG9mIHZhbHVlcywgdGhlIHNwcmVhZCwgYW5kIHRoZSBwcmVzZW5jZSBvZiBwb3RlbnRpYWwgb3V0bGllcnMuIFRoZXNlIHZpc3VhbGl6YXRpb25zIHNlcnZlIGFzIGEgdmFsdWFibGUgdG9vbCBmb3IgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyAoRURBKSBhbmQgcHJlcGFyaW5nIGRhdGEgZm9yIGZ1cnRoZXIgbW9kZWxpbmcuCgpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTh9CiMgU3Vic2V0IHRoZSBjbGVhbmVkIGRhdGFzZXQgdG8gdGhlIHNlbGVjdGVkIGNvbHVtbnMKY29ycmVsYXRpb25fZGF0YSA8LSBjbGVhbmVkX2RmX2NvbHVtbndpc2VbLCBzZWxlY3RlZF9jb2xzXQoKIyBDb21wdXRlIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgKY29ycmVsYXRpb25fbWF0cml4IDwtIGNvcihjb3JyZWxhdGlvbl9kYXRhLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKCiMgTWVsdCB0aGUgY29ycmVsYXRpb24gbWF0cml4IGZvciBnZ3Bsb3QKY29ycmVsYXRpb25fbWVsdGVkIDwtIG1lbHQoY29ycmVsYXRpb25fbWF0cml4KQoKIyBHZW5lcmF0ZSB0aGUgY29ycmVsYXRpb24gaGVhdG1hcApnZ3Bsb3QoZGF0YSA9IGNvcnJlbGF0aW9uX21lbHRlZCwgYWVzKHggPSBWYXIxLCB5ID0gVmFyMiwgZmlsbCA9IHZhbHVlKSkgKwogIGdlb21fdGlsZSgpICsgIyBBZGQgd2hpdGUgYm9yZGVyIGZvciB0aWxlcwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZCh2YWx1ZSwgMikpLCBzaXplID0gNikgKyAjIEFkZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMKICBzY2FsZV9maWxsX2dyYWRpZW50MigKICAgIGxvdyA9IGNvbG9yc1szXSwgCiAgICBoaWdoID0gY29sb3JzWzFdLCAKICAgIG1pZCA9IGNvbG9yc1syXSwgCiAgICBtaWRwb2ludCA9IDAsIAogICAgbGltaXRzID0gYygtMSwgMSksCiAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKAogICAgICB0aXRsZSA9ICIiLAogICAgICBiYXJ3aWR0aCA9IDQwLCAjIE1ha2UgdGhlIGNvbG9yIGJhciB3aWRlcgogICAgICBiYXJoZWlnaHQgPSAxICAjIEFkanVzdCB0aGUgaGVpZ2h0IG9mIHRoZSBiYXIKICAgICkKICApICsKICB0aGVtZV9yb3NlX3BpbmUoKSArICMgQXBwbHkgdGhlIFJvc8OpIFBpbmUgdGhlbWUKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDE2KSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjI4KSwgIyBDZW50ZXIgYW5kIGFkanVzdCB0aXRsZSBzaXplCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92ZSBheGlzIHRpdGxlcwogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgIyBSZW1vdmUgZ3JpZCBsaW5lcwogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLCAjIEFkanVzdCBsZWdlbmQgdGV4dCBzaXplCiAgKSsKICBnZ3RpdGxlKCJDb3JyZWxhdGlvbiBNYXRyaXgiKSAjIEFkZCBwbG90IHRpdGxlCmBgYApgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTh9CiMgU2VsZWN0IGNhdGVnb3JpY2FsIGNvbHVtbnMKY2F0ZWdvcmljYWxfY29scyA8LSBjKCJHZW5kZXIiLCAiTG9jYXRpb24iLCAiQ29udGFjdF9BbGxvd2VkIiwgIk1hcmtldGluZ19DaGFubmVsX1R5cGUiKQoKIyBTdWJzZXQgdGhlIGRhdGFzZXQKY2F0ZWdvcmljYWxfZGF0YSA8LSBjbGVhbmVkX2RmX2NvbHVtbndpc2VbLCBjYXRlZ29yaWNhbF9jb2xzXQoKIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBjb21wdXRlIENyYW3DqXIncyBWIGZvciBhIHBhaXIgb2YgY2F0ZWdvcmljYWwgZmVhdHVyZXMKY3JhbWVyc192X21hdHJpeCA8LSBmdW5jdGlvbihkYXRhKSB7CiAgbiA8LSBuY29sKGRhdGEpCiAgbWF0cml4IDwtIG1hdHJpeChOQSwgbiwgbiwgZGltbmFtZXMgPSBsaXN0KG5hbWVzKGRhdGEpLCBuYW1lcyhkYXRhKSkpCiAgZm9yIChpIGluIHNlcV9sZW4obikpIHsKICAgIGZvciAoaiBpbiBzZXFfbGVuKG4pKSB7CiAgICAgIGlmIChpIDw9IGopIHsKICAgICAgICBtYXRyaXhbaSwgal0gPC0gY3JhbWVyc1YoZGF0YVtbaV1dLCBkYXRhW1tqXV0sIHNpbXVsYXRlLnAudmFsdWUgPSBUUlVFKQogICAgICB9IGVsc2UgewogICAgICAgIG1hdHJpeFtpLCBqXSA8LSBtYXRyaXhbaiwgaV0KICAgICAgfQogICAgfQogIH0KICByZXR1cm4obWF0cml4KQp9CgojIENvbXB1dGUgdGhlIENyYW3DqXIncyBWIG1hdHJpeApjcmFtZXJzX3YgPC0gY3JhbWVyc192X21hdHJpeChjYXRlZ29yaWNhbF9kYXRhKQoKIyBNZWx0IHRoZSBtYXRyaXggZm9yIGdncGxvdAptZWx0ZWRfY3JhbWVyc192IDwtIG1lbHQoY3JhbWVyc192LCBuYS5ybSA9IFRSVUUpCgojIEdlbmVyYXRlIHRoZSBoZWF0bWFwCmdncGxvdChkYXRhID0gbWVsdGVkX2NyYW1lcnNfdiwgYWVzKHggPSBWYXIxLCB5ID0gVmFyMiwgZmlsbCA9IHZhbHVlKSkgKwogIGdlb21fdGlsZSgpICsgIyBBZGQgd2hpdGUgYm9yZGVycyBmb3IgdGlsZXMKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQodmFsdWUsIDIpKSwgc2l6ZSA9IDYpICsgIyBBZGQgQ3JhbcOpcidzIFYgdmFsdWVzCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICBsb3cgPSBjb2xvcnNbMl0sIAogICAgaGlnaCA9IGNvbG9yc1sxXSwgCiAgICBtaWQgPSBjb2xvcnNbM10sIAogICAgbWlkcG9pbnQgPSAwLjUsCiAgICBsaW1pdHMgPSBjKDAsIDEpLAogICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcigKICAgICAgdGl0bGUgPSAiIiwKICAgICAgYmFyd2lkdGggPSAzMCwgIyBBZGp1c3QgY29sb3IgYmFyIHdpZHRoCiAgICAgIGJhcmhlaWdodCA9IDEgICMgQWRqdXN0IGNvbG9yIGJhciBoZWlnaHQKICAgICkKICApICsKICB0aGVtZV9yb3NlX3BpbmUoYmFzZV9zaXplID0gMTQpICsgIyBBcHBseSBSb3PDqSBQaW5lIHRoZW1lCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxNiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwgIyBDZW50ZXIgdGhlIHRpdGxlCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92ZSBheGlzIHRpdGxlcwogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgIyBSZW1vdmUgZ3JpZCBsaW5lcwogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAjIEFkanVzdCBsZWdlbmQgdGV4dCBzaXplCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKQogICkgKwogIGdndGl0bGUoIkNyYW3DqXIncyBWIENvcnJlbGF0aW9uIE1hdHJpeCBmb3IgQ2F0ZWdvcmljYWwgRmVhdHVyZXMiKSAjIEFkZCBwbG90IHRpdGxlCmBgYAoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0KZ2dwYWlycygKICBkYXRhID0gY2xlYW5lZF9kZl9jb2x1bW53aXNlWywgc2VsZWN0ZWRfY29sc10sCiAgbWFwcGluZyA9IGFlcyhjb2xvciA9ICJUb3RhbF9Qcm9maXQiKSwgIyBSZXBsYWNlIHdpdGggYSByZWxldmFudCBjb2x1bW4gaWYgbmVlZGVkCiAgbG93ZXIgPSBsaXN0KAogICAgY29udGludW91cyA9IHdyYXAoInNtb290aCIsIGNvbG9yID0gY29sb3JzWzJdLCBzaXplID0gMC44KSwKICAgIGNvbWJvID0gd3JhcCgiZmFjZXRkZW5zaXR5IiwgYWxwaGEgPSAwLjcsIGZpbGwgPSBjb2xvcnNbM10pCiAgKSwKICB1cHBlciA9IGxpc3QoCiAgICBjb250aW51b3VzID0gd3JhcCgiY29yIiwgc2l6ZSA9IDYsIGNvbG9yID0gcm9zZV9waW5lX2NvbG9ycyR0ZXh0KQogICksCiAgZGlhZyA9IGxpc3QoCiAgICBjb250aW51b3VzID0gd3JhcCgiZGVuc2l0eURpYWciLCBmaWxsID0gY29sb3JzWzFdLCBhbHBoYSA9IDAuNykKICApCikgKwogIHRoZW1lX3Jvc2VfcGluZShiYXNlX3NpemUgPSAxMikgKyAjIEFwcGx5IHRoZSBSb3PDqSBQaW5lIHRoZW1lCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCwgZmFjZSA9ICJib2xkIiksICMgVGl0bGUgZm9udCBzaXplCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwgIyBBeGlzIHRleHQgc2l6ZQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpLCAjIEF4aXMgdGl0bGUgc2l6ZQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpICMgU2l6ZSBvZiBmYWNldCBsYWJlbHMKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiUGFpcnBsb3Qgd2l0aCBDb3JyZWxhdGlvbiIKICApCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD04fQojIFBhcnNlIFJlZ2lzdHJhdGlvbl9EYXRlIGFzIGRhdGV0aW1lCmNsZWFuZWRfZGZfY29sdW1ud2lzZSRSZWdpc3RyYXRpb25fRGF0ZSA8LSBtZHlfaG0oY2xlYW5lZF9kZl9jb2x1bW53aXNlJFJlZ2lzdHJhdGlvbl9EYXRlKQoKIyBFeHRyYWN0IHRoZSBtb250aCBhbmQgeWVhcgpjbGVhbmVkX2RmX2NvbHVtbndpc2UgPC0gY2xlYW5lZF9kZl9jb2x1bW53aXNlICU+JQogIG11dGF0ZShNb250aCA9IG1vbnRoKFJlZ2lzdHJhdGlvbl9EYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgIyBlLmcuLCBKYW4sIEZlYgoKbW9udGhseV9wcm9maXQgPC0gY2xlYW5lZF9kZl9jb2x1bW53aXNlICU+JQogIGdyb3VwX2J5KE1vbnRoKSAlPiUKICBzdW1tYXJpc2UoVG90YWxfUHJvZml0ID0gc3VtKFRvdGFsX1Byb2ZpdCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShNb250aCkKCmdncGxvdChtb250aGx5X3Byb2ZpdCwgYWVzKHggPSBNb250aCwgeSA9IFRvdGFsX1Byb2ZpdCwgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZShjb2xvciA9IGNvbG9yc1sxXSwgc2l6ZSA9IDEuMiwgbGluZXR5cGU9InR3b2Rhc2giKSArICMgTGluZSB3aXRoIGN1c3RvbSBjb2xvcgogIGdlb21fcG9pbnQoY29sb3IgPSBjb2xvcnNbMl0sIHNpemUgPSAzKSArICMgUG9pbnRzIG9uIHRoZSBsaW5lCiAgbGFicygKICAgIHRpdGxlID0gIk1vbnRobHkgVG90YWwgUHJvZml0IGluIDIwMTMiLAogICAgeCA9ICJNb250aCIsCiAgICB5ID0gIlRvdGFsIFByb2ZpdCIKICApICsKICB0aGVtZV9yb3NlX3BpbmUoYmFzZV9zaXplID0gMTUpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAjIENlbnRlcmVkIHRpdGxlCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICkKCmBgYApgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTZ9CiMgR3JvdXAgZGF0YSBieSBNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlIGFuZCBNb250aCwgdGhlbiBjYWxjdWxhdGUgdGhlIHRvdGFsIHByb2ZpdAptb250aGx5X3Byb2ZpdF9ieV9jaGFubmVsIDwtIGNsZWFuZWRfZGZfY29sdW1ud2lzZSAlPiUKICBncm91cF9ieShNb250aCwgTWFya2V0aW5nX0NoYW5uZWxfVHlwZSkgJT4lCiAgc3VtbWFyaXNlKFRvdGFsX1Byb2ZpdCA9IHN1bShUb3RhbF9Qcm9maXQsIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoTW9udGgpCgojIENoZWNrIHRoZSB1bmlxdWUgbWFya2V0aW5nIGNoYW5uZWxzIGFuZCBjb2xvcnMKdW5pcXVlX2NoYW5uZWxzIDwtIHVuaXF1ZShtb250aGx5X3Byb2ZpdF9ieV9jaGFubmVsJE1hcmtldGluZ19DaGFubmVsX1R5cGUpCgojIEVuc3VyZSB0aGUgY29sb3JzIG1hdGNoIHRoZSB1bmlxdWUgY2hhbm5lbHMKY29sb3JzX2Zvcl9jaGFubmVscyA8LSBzZXROYW1lcyhjb2xvcnNbMTpsZW5ndGgodW5pcXVlX2NoYW5uZWxzKV0sIHVuaXF1ZV9jaGFubmVscykKCiMgUGxvdCB3aXRoIGNvbG9yIG1hcHBpbmcKZ2dwbG90KG1vbnRobHlfcHJvZml0X2J5X2NoYW5uZWwsIGFlcyh4ID0gTW9udGgsIHkgPSBUb3RhbF9Qcm9maXQsIGNvbG9yID0gTWFya2V0aW5nX0NoYW5uZWxfVHlwZSwgZ3JvdXAgPSBNYXJrZXRpbmdfQ2hhbm5lbF9UeXBlKSkgKwogIGdlb21fbGluZShzaXplID0gMS4yLCBsaW5ldHlwZT0idHdvZGFzaCIpICsgIyBMaW5lIGZvciBlYWNoIG1hcmtldGluZyBjaGFubmVsCiAgZ2VvbV9wb2ludChzaXplID0gMykgKyAjIFBvaW50cyBvbiB0aGUgbGluZXMKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzX2Zvcl9jaGFubmVscykgKyAjIE1hcCBjb2xvcnMgdG8gY2hhbm5lbHMKICBsYWJzKAogICAgdGl0bGUgPSAiTW9udGhseSBUb3RhbCBQcm9maXQgYnkgTWFya2V0aW5nIENoYW5uZWwgaW4gMjAxMyIsCiAgICB4ID0gIk1vbnRoIiwKICAgIHkgPSAiVG90YWwgUHJvZml0IiwKICAgIGNvbG9yID0gIk1hcmtldGluZyBDaGFubmVsIiAjIExlZ2VuZCB0aXRsZQogICkgKwogIHRoZW1lX3Jvc2VfcGluZShiYXNlX3NpemUgPSAxNSkgKyAjIEFwcGx5IFJvc8OpIFBpbmUgdGhlbWUKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAjIENlbnRlcmVkIHRpdGxlCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsICMgTGVnZW5kIHBvc2l0aW9uCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAjIExlZ2VuZCB0ZXh0IHNpemUKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpICMgTGVnZW5kIHRpdGxlIHNpemUKICApCmBgYAoKIyA1LiBDb25jbHVzaW9uCgpUaGlzIHJlcG9ydCBoaWdobGlnaHRzIHRoZSBrZXkgbWV0cmljcywgbWFqb3IgZGF0YSBwb2ludHMsIGFuZCBjb3JyZWxhdGlvbnMgZnJvbSB0aGUgZGF0YXNldC4gVGhlIHZpc3VhbGl6YXRpb24gcHJvdmlkZXMgYSBjbGVhciBwaWN0dXJlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgZGlmZmVyZW50IGF0dHJpYnV0ZXMuCg==